efi: Add 64-bit payload support
authorSimon Glass <sjg@chromium.org>
Tue, 4 Aug 2015 18:33:56 +0000 (12:33 -0600)
committerSimon Glass <sjg@chromium.org>
Wed, 5 Aug 2015 14:44:07 +0000 (08:44 -0600)
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>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Bin Meng <bmeng.cn@gmail.com>
Makefile
arch/x86/config.mk
arch/x86/include/asm/types.h
arch/x86/lib/efi/crt0-efi-x86_64.S [new file with mode: 0644]
include/efi.h
lib/efi/efi_stub.c

index 752ee0dbeaac6c7f7879883e98ea15cd8378562d..bb0ba9f8a22473e297b77ef8a4ec1ff8b3bc0fff 100644 (file)
--- 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))
 
index 334c10b882d156751475f70eef9fd4b5ad201881..d7addd872866a7e752ae2be4856157fe5bd9242c 100644 (file)
@@ -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)
index e272c90eb7f6bd3ef52bfb5c1171aab136110c27..766617f9aa3e22a5ac2346acac4312104e7821ff 100644 (file)
@@ -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 (file)
index 0000000..c5cbf41
--- /dev/null
@@ -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 */
index 1470c0825c4b6f394b5b48cdf42f33acddc95a44..fcafda0b0bf5fed0ac9adfce5dd9d8dfb7eb080c 100644 (file)
 #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
index 1e46f6ece14fe9b3ac459f2d20288d55796ec27a..d4d3e496899fafe201416e9f1a24712cb9653cf5 100644 (file)
@@ -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;
 }