From 96a8d409a75af99ac7a9a9ba707d544f9cf44fc0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 4 Aug 2015 12:33:56 -0600 Subject: [PATCH] efi: Add 64-bit payload support 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 Improvements to how the payload is built: Signed-off-by: Bin Meng Reviewed-by: Bin Meng Tested-by: Bin Meng --- 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 752ee0dbea..bb0ba9f8a2 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 334c10b882..d7addd8728 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 e272c90eb7..766617f9aa 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 0000000000..c5cbf4108b --- /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 . + * Copyright (C) 2005 Intel Co. + * Contributed by Fenghua Yu . + * + * 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 1470c0825c..fcafda0b0b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -18,6 +18,13 @@ #include #include +#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 1e46f6ece1..d4d3e49689 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 @@ -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; } -- 2.39.5