[U-Boot] [PATCH v2 38/47] efi: Add 64-bit payload support

Simon Glass sjg at chromium.org
Fri Jul 31 17:31:55 CEST 2015


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 at chromium.org>
Improvements to how the payload is built:
Signed-off-by: Bin Meng <bmeng.cn at gmail.com>
---

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, 142 insertions(+), 7 deletions(-)
 create mode 100644 arch/x86/lib/efi/crt0-efi-x86_64.S

diff --git a/Makefile b/Makefile
index 755eec8..e591a6d 100644
--- a/Makefile
+++ b/Makefile
@@ -1096,7 +1096,7 @@ u-boot-payload.lds: $(LDSCRIPT_EFI) FORCE
 u-boot-payload: u-boot-dtb.bin.o u-boot-payload.lds \
 		FORCE
 	$(LD) $(LDFLAGS_EFI) -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 48eb5f5..83fc424 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 at hpl.hp.com>.
+ * Copyright (C) 2005 Intel Co.
+ * Contributed by Fenghua Yu <fenghua.yu at 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 34844e4..4932ee1 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 7259720..d49804a 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>
@@ -116,12 +116,12 @@ void *memset(void *inptr, int ch, size_t size)
 
 static void jump_to_uboot(ulong cs32, ulong addr, ulong info)
 {
+#ifdef CONFIG_EFI_STUB_32BIT
 	typedef void (*func_t)(int bist, int unused, ulong info);
 
-#ifdef CONFIG_EFI_STUB_32BIT
 	((func_t)addr)(bist, 0, info);
 #else
-	/* TODO: Implement this */
+	cpu_call32(cs32, CONFIG_SYS_TEXT_BASE, info);
 #endif
 }
 
@@ -151,7 +151,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 + 0x100000 < 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.5.0.rc2.392.g76e840b



More information about the U-Boot mailing list