
On Wed, Aug 5, 2015 at 2:33 AM, Simon Glass sjg@chromium.org wrote:
Most EFI implementations use 64-bit. Add a way to build U-Boot as a 64-bit EFI payload. The payload unpacks a (32-bit) U-Boot and starts it. This can be enabled for x86 boards at present.
Signed-off-by: Simon Glass sjg@chromium.org Improvements to how the payload is built: Signed-off-by: Bin Meng bmeng.cn@gmail.com
Changes in v3:
- Add spaces around EFIARCH=
- Use CONFIG_SYS_MONITOR_LEN as a more accurate value for U-Boot's size
Changes in v2:
- Add -no-red-zone for 64-bit only
- Check the GDT selector's base and limit against the target address
- Drop use of CONFIG_X86_64 since we don't support a 64-bit EFI application yet
- Merge in Bin's implementation of adding a U-Boot payload with objcopy
- Move the 64-bit crt and reloc code into this patch
- Move the 64-bit efi.h additions into this patch
- Rename GDT_4GB to GDT_4KB
Makefile | 2 +- arch/x86/config.mk | 10 ++++++ arch/x86/include/asm/types.h | 5 ++- arch/x86/lib/efi/crt0-efi-x86_64.S | 51 ++++++++++++++++++++++++++ include/efi.h | 7 ++++ lib/efi/efi_stub.c | 74 +++++++++++++++++++++++++++++++++++--- 6 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 arch/x86/lib/efi/crt0-efi-x86_64.S
diff --git a/Makefile b/Makefile index 752ee0d..bb0ba9f 100644 --- a/Makefile +++ b/Makefile @@ -1100,7 +1100,7 @@ u-boot-payload.lds: $(LDSCRIPT_EFI) FORCE # Rule to link the EFI payload which contains a stub and a U-Boot binary quiet_cmd_u-boot_payload ?= LD $@ cmd_u-boot_payload ?= $(LD) $(LDFLAGS_EFI_PAYLOAD) -o $@ \
-T u-boot-payload.lds \
-T u-boot-payload.lds arch/x86/cpu/call32.o \ lib/efi/efi.o lib/efi/efi_stub.o u-boot-dtb.bin.o \ $(addprefix arch/$(ARCH)/lib/efi/,$(EFISTUB))
diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 334c10b..d7addd8 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -34,14 +34,24 @@ OBJCOPYFLAGS_EFI := -j .text -j .sdata -j .data -j .dynamic -j .dynsym \ CFLAGS_NON_EFI := -mregparm=3 CFLAGS_EFI := -fpic -fshort-wchar
+ifeq ($(CONFIG_EFI_STUB_64BIT),) +CFLAGS_EFI += $(call cc-option, -mno-red-zone) EFIARCH = ia32 EFIPAYLOAD_BFDTARGET = elf32-i386 +else +EFIARCH = x86_64 +EFIPAYLOAD_BFDTARGET = elf64-x86-64 +endif
EFIPAYLOAD_BFDARCH = i386
LDSCRIPT_EFI := $(srctree)/$(CPUDIR)/efi/elf_$(EFIARCH)_efi.lds +EFISTUB := crt0-efi-$(EFIARCH).o reloc_$(EFIARCH).o OBJCOPYFLAGS_EFI += --target=efi-app-$(EFIARCH)
+CPPFLAGS_REMOVE_crt0-efi-$(EFIARCH).o += $(CFLAGS_NON_EFI) +CPPFLAGS_crt0-efi-$(EFIARCH).o += $(CFLAGS_EFI)
ifeq ($(CONFIG_EFI_APP),y)
PLATFORM_CPPFLAGS += $(CFLAGS_EFI) diff --git a/arch/x86/include/asm/types.h b/arch/x86/include/asm/types.h index e272c90..766617f 100644 --- a/arch/x86/include/asm/types.h +++ b/arch/x86/include/asm/types.h @@ -44,8 +44,11 @@ typedef __INT64_TYPE__ s64; typedef __UINT64_TYPE__ u64; #endif
+#ifdef CONFIG_EFI_STUB_64BIT +#define BITS_PER_LONG 64 +#else #define BITS_PER_LONG 32
+#endif /* Dma addresses are 32-bits wide. */
typedef u32 dma_addr_t; diff --git a/arch/x86/lib/efi/crt0-efi-x86_64.S b/arch/x86/lib/efi/crt0-efi-x86_64.S new file mode 100644 index 0000000..c5cbf41 --- /dev/null +++ b/arch/x86/lib/efi/crt0-efi-x86_64.S @@ -0,0 +1,51 @@ +/*
- crt0-efi-x86_64.S - x86_64 EFI startup code.
- Copyright (C) 1999 Hewlett-Packard Co.
- Contributed by David Mosberger davidm@hpl.hp.com.
- Copyright (C) 2005 Intel Co.
- Contributed by Fenghua Yu fenghua.yu@intel.com.
- All rights reserved.
- SPDX-License-Identifier: BSD-3-Clause
- */
.text
.align 4
.globl _start
+_start:
subq $8, %rsp
pushq %rcx
pushq %rdx
+0:
lea image_base(%rip), %rdi
lea _DYNAMIC(%rip), %rsi
popq %rcx
popq %rdx
pushq %rcx
pushq %rdx
call _relocate
popq %rdi
popq %rsi
call efi_main
addq $8, %rsp
+.exit:
ret
/*
* hand-craft a dummy .reloc section so EFI knows it's a relocatable
* executable:
*/
.data
+dummy: .long 0
+#define IMAGE_REL_ABSOLUTE 0
.section .reloc, "a"
+label1:
.long dummy-label1 /* Page RVA */
.long 10 /* Block Size (2*4+2) */
.word (IMAGE_REL_ABSOLUTE << 12) + 0 /* reloc for dummy */
diff --git a/include/efi.h b/include/efi.h index 1470c08..fcafda0 100644 --- a/include/efi.h +++ b/include/efi.h @@ -18,6 +18,13 @@ #include <linux/string.h> #include <linux/types.h>
+#ifdef CONFIG_EFI_STUB_64BIT +/* EFI uses the Microsoft ABI which is not the default for GCC */ +#define EFIAPI __attribute__((ms_abi)) +#else +#define EFIAPI +#endif
struct efi_device_path;
#define EFI_SUCCESS 0 diff --git a/lib/efi/efi_stub.c b/lib/efi/efi_stub.c index 1e46f6e..d4d3e49 100644 --- a/lib/efi/efi_stub.c +++ b/lib/efi/efi_stub.c @@ -6,8 +6,8 @@
- EFI information obtained here:
- http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
- Loads a payload (U-Boot) within the EFI environment. This is built as a
- 32-bit EFI application.
- Loads a payload (U-Boot) within the EFI environment. This is built as an
*/
- EFI application. It can be built either in 32-bit or 64-bit mode.
#include <common.h> @@ -126,14 +126,16 @@ static void jump_to_uboot(ulong cs32, ulong addr, ulong info)
((func_t)addr)(0, 0, info);
#else
/* TODO: Implement this */
cpu_call32(cs32, CONFIG_SYS_TEXT_BASE, info);
#endif }
+#ifdef CONFIG_EFI_STUB_64BIT static void get_gdt(struct desctab_info *info) { asm volatile ("sgdt %0" : : "m"(*info) : "memory"); } +#endif
static inline unsigned long read_cr3(void) { @@ -156,7 +158,71 @@ static int get_codeseg32(void) { int cs32 = 0;
/* TODO(sjg): Implement this for 64-bit mode */
+#ifdef CONFIG_EFI_STUB_64BIT
struct desctab_info gdt;
uint64_t *ptr;
int i;
get_gdt(&gdt);
for (ptr = (uint64_t *)(unsigned long)gdt.addr, i = 0; i < gdt.limit;
i += 8, ptr++) {
uint64_t desc = *ptr;
uint64_t base, limit;
/*
* Check that the target U-Boot jump address is within the
* selector and that the selector is of the right type.
*/
base = ((desc >> GDT_BASE_LOW_SHIFT) & GDT_BASE_LOW_MASK) |
((desc >> GDT_BASE_HIGH_SHIFT) & GDT_BASE_HIGH_MASK)
<< 16;
limit = ((desc >> GDT_LIMIT_LOW_SHIFT) & GDT_LIMIT_LOW_MASK) |
((desc >> GDT_LIMIT_HIGH_SHIFT) & GDT_LIMIT_HIGH_MASK)
<< 16;
base <<= 12; /* 4KB granularity */
limit <<= 12;
if ((desc & GDT_PRESENT) && (desc && GDT_NOTSYS) &&
!(desc & GDT_LONG) && (desc & GDT_4KB) &&
(desc & GDT_32BIT) && (desc & GDT_CODE) &&
CONFIG_SYS_TEXT_BASE > base &&
CONFIG_SYS_TEXT_BASE + CONFIG_SYS_MONITOR_LEN < limit
) {
cs32 = i;
break;
}
}
+#ifdef DEBUG
puts("\ngdt: ");
printhex8(gdt.limit);
puts(", addr: ");
printhex8(gdt.addr >> 32);
printhex8(gdt.addr);
for (i = 0; i < gdt.limit; i += 8) {
uint32_t *ptr = (uint32_t *)((unsigned long)gdt.addr + i);
puts("\n");
printhex2(i);
puts(": ");
printhex8(ptr[1]);
puts(" ");
printhex8(ptr[0]);
}
puts("\n ");
puts("32-bit code segment: ");
printhex2(cs32);
puts("\n ");
puts("page_table: ");
printhex8(read_cr3());
puts("\n ");
+#endif
if (!cs32) {
puts("Can't find 32-bit code segment\n");
return -ENOENT;
}
+#endif
return cs32;
}
--
Reviewed-by: Bin Meng bmeng.cn@gmail.com
Tested on QEMU 32-bit and 64-bit Tested-by: Bin Meng bmeng.cn@gmail.com