Message ID | 1438713246-1887-19-git-send-email-sjg@chromium.org |
---|---|
State | Accepted |
Delegated to: | Simon Glass |
Headers | show |
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>
On 5 August 2015 at 02:02, Bin Meng <bmeng.cn@gmail.com> wrote: > 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 >> [snip] > > Reviewed-by: Bin Meng <bmeng.cn@gmail.com> > > Tested on QEMU 32-bit and 64-bit > Tested-by: Bin Meng <bmeng.cn@gmail.com>
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; }