x86: Add support for U-Boot as an EFI application

Add the required x86 glue code. This includes the initial start-up,
relocation and jumping to efi_main(). We also need to avoid fiddling with
interrupts.

Signed-off-by: Ben Stoltz <stoltz@google.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index e8968a7..7e6e89c 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -11,6 +11,9 @@
 config VENDOR_COREBOOT
 	bool "coreboot"
 
+config VENDOR_EFI
+	bool "efi"
+
 config VENDOR_EMULATION
 	bool "emulation"
 
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 36a6018..d104a49 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -2,7 +2,9 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+ifeq ($(CONFIG_EFI_APP),)
 head-y := arch/x86/cpu/start.o
+endif
 ifeq ($(CONFIG_SPL_BUILD),y)
 head-y += arch/x86/cpu/start16.o
 head-y += arch/x86/cpu/resetvec.o
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 8a8e63e..5e058c0 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -14,6 +14,7 @@
 
 obj-$(CONFIG_INTEL_BAYTRAIL) += baytrail/
 obj-$(CONFIG_SYS_COREBOOT) += coreboot/
+obj-$(CONFIG_EFI_APP) += efi/
 obj-$(CONFIG_QEMU) += qemu/
 obj-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += ivybridge/
 obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
diff --git a/arch/x86/cpu/efi/Makefile b/arch/x86/cpu/efi/Makefile
new file mode 100644
index 0000000..e091637
--- /dev/null
+++ b/arch/x86/cpu/efi/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (c) 2015 Google, Inc
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y += efi.o
+obj-y += sdram.o
diff --git a/arch/x86/cpu/efi/efi.c b/arch/x86/cpu/efi/efi.c
new file mode 100644
index 0000000..75ba0d4
--- /dev/null
+++ b/arch/x86/cpu/efi/efi.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <netdev.h>
+
+int arch_cpu_init(void)
+{
+#ifdef CONFIG_SYS_X86_TSC_TIMER
+	timer_set_base(rdtsc());
+#endif
+
+	return 0;
+}
+
+int board_early_init_f(void)
+{
+	return 0;
+}
+
+int print_cpuinfo(void)
+{
+	return default_print_cpuinfo();
+}
+
+void board_final_cleanup(void)
+{
+}
+
+int misc_init_r(void)
+{
+	return 0;
+}
+
+int arch_misc_init(void)
+{
+	return 0;
+}
diff --git a/arch/x86/cpu/efi/elf_ia32_efi.lds b/arch/x86/cpu/efi/elf_ia32_efi.lds
new file mode 100644
index 0000000..cd3b0a9
--- /dev/null
+++ b/arch/x86/cpu/efi/elf_ia32_efi.lds
@@ -0,0 +1,94 @@
+/*
+ * U-Boot EFI linker script
+ *
+ * SPDX-License-Identifier:	BSD-2-Clause
+ *
+ * Modified from usr/lib32/elf_ia32_efi.lds in gnu-efi
+ */
+
+#include <config.h>
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+	image_base = .;
+	.hash : { *(.hash) }	/* this MUST come first, EFI expects it */
+	. = ALIGN(4096);
+	.text :
+	{
+		*(.text)
+		*(.text.*)
+		*(.gnu.linkonce.t.*)
+	}
+	. = ALIGN(4096);
+	.sdata :
+	{
+		*(.got.plt)
+		*(.got)
+		*(.srodata)
+		*(.sdata)
+		*(.sbss)
+		*(.scommon)
+	}
+	. = ALIGN(4096);
+	.data :
+	{
+		*(.rodata*)
+		*(.data)
+		*(.data1)
+		*(.data.*)
+		*(.sdata)
+		*(.got.plt)
+		*(.got)
+		/*
+		 * the EFI loader doesn't seem to like a .bss section, so we
+		 * stick it all into .data:
+		 */
+		*(.sbss)
+		*(.scommon)
+		*(.dynbss)
+		*(.bss)
+		*(COMMON)
+
+		/* U-Boot lists and device tree */
+		. = ALIGN(8);
+		*(SORT(.u_boot_list*));
+		. = ALIGN(8);
+		*(.dtb*);
+	}
+
+	. = ALIGN(4096);
+	.dynamic  : { *(.dynamic) }
+	. = ALIGN(4096);
+	.rel :
+	{
+		*(.rel.data)
+		*(.rel.data.*)
+		*(.rel.got)
+		*(.rel.stab)
+		*(.data.rel.ro.local)
+		*(.data.rel.local)
+		*(.data.rel.ro)
+		*(.data.rel*)
+		*(.rel.u_boot_list*)
+	}
+	. = ALIGN(4096);
+		.reloc :	/* This is the PECOFF .reloc section! */
+	{
+	*(.reloc)
+	}
+	. = ALIGN(4096);
+	.dynsym   : { *(.dynsym) }
+	. = ALIGN(4096);
+	.dynstr   : { *(.dynstr) }
+	. = ALIGN(4096);
+	/DISCARD/ :
+	{
+		*(.rel.reloc)
+		*(.eh_frame)
+		*(.note.GNU-stack)
+	}
+	.comment 0 : { *(.comment) }
+}
diff --git a/arch/x86/cpu/efi/sdram.c b/arch/x86/cpu/efi/sdram.c
new file mode 100644
index 0000000..5159944
--- /dev/null
+++ b/arch/x86/cpu/efi/sdram.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi.h>
+#include <asm/u-boot-x86.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+ulong board_get_usable_ram_top(ulong total_size)
+{
+	return (ulong)efi_get_ram_base() + gd->ram_size;
+}
+
+int dram_init(void)
+{
+	/* gd->ram_size is set as part of EFI init */
+
+	return 0;
+}
+
+void dram_init_banksize(void)
+{
+	gd->bd->bi_dram[0].start = efi_get_ram_base();
+	gd->bd->bi_dram[0].size = CONFIG_EFI_RAM_SIZE;
+}
diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c
index 3a9c2d4..a112938 100644
--- a/arch/x86/cpu/interrupts.c
+++ b/arch/x86/cpu/interrupts.c
@@ -242,6 +242,11 @@
 
 int interrupt_init(void)
 {
+	/*
+	 * When running as an EFI application we are not in control of
+	 * interrupts and should leave them alone.
+	 */
+#ifndef CONFIG_EFI_APP
 	/* Just in case... */
 	disable_interrupts();
 
@@ -255,6 +260,7 @@
 
 	/* It is now safe to enable interrupts */
 	enable_interrupts();
+#endif
 
 	return 0;
 }
diff --git a/arch/x86/include/asm/arch-efi/gpio.h b/arch/x86/include/asm/arch-efi/gpio.h
new file mode 100644
index 0000000..f044f07
--- /dev/null
+++ b/arch/x86/include/asm/arch-efi/gpio.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _X86_ARCH_GPIO_H_
+#define _X86_ARCH_GPIO_H_
+
+#endif /* _X86_ARCH_GPIO_H_ */
diff --git a/arch/x86/lib/efi/crt0-efi-ia32.S b/arch/x86/lib/efi/crt0-efi-ia32.S
new file mode 100644
index 0000000..30e5eb0
--- /dev/null
+++ b/arch/x86/lib/efi/crt0-efi-ia32.S
@@ -0,0 +1,52 @@
+/*
+ * crt0-efi-ia32.S - x86 EFI startup code.
+ *
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Contributed by David Mosberger <davidm@hpl.hp.com>.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+	.text
+	.align 4
+
+	.globl _start
+_start:
+	pushl %ebp
+	movl %esp,%ebp
+
+	pushl 12(%ebp)			# copy "image" argument
+	pushl  8(%ebp)			# copy "systab" argument
+
+	call 0f
+0:	popl %eax
+	movl %eax,%ebx
+
+	addl $image_base-0b,%eax	# %eax = ldbase
+	addl $_DYNAMIC-0b,%ebx		# %ebx = _DYNAMIC
+
+	pushl %ebx			# pass _DYNAMIC as second argument
+	pushl %eax			# pass ldbase as first argument
+	call _relocate
+	popl %ebx
+	popl %ebx
+	testl %eax,%eax
+	jne .exit
+	call efi_main		# call app with "image" and "systab" argument
+
+.exit:	leave
+	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
+	.long	dummy					/* Page RVA */
+	.long	10					/* Block Size (2*4+2) */
+	.word	(IMAGE_REL_ABSOLUTE << 12) +  0		/* reloc for dummy */
diff --git a/arch/x86/lib/efi/reloc_ia32.c b/arch/x86/lib/efi/reloc_ia32.c
new file mode 100644
index 0000000..4d68255
--- /dev/null
+++ b/arch/x86/lib/efi/reloc_ia32.c
@@ -0,0 +1,72 @@
+/*
+ * reloc_ia32.c - position independent x86 ELF shared object relocator
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Contributed by David Mosberger <davidm@hpl.hp.com>.
+ *
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include <common.h>
+#include <efi.h>
+#include <elf.h>
+#include <asm/elf.h>
+
+efi_status_t _relocate(long ldbase, Elf32_Dyn *dyn, efi_handle_t image,
+		       struct efi_system_table *systab)
+{
+	long relsz = 0, relent = 0;
+	Elf32_Rel *rel = 0;
+	unsigned long *addr;
+	int i;
+
+	for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
+		switch (dyn[i].d_tag) {
+		case DT_REL:
+			rel = (Elf32_Rel *)((unsigned long)dyn[i].d_un.d_ptr +
+								ldbase);
+			break;
+
+		case DT_RELSZ:
+			relsz = dyn[i].d_un.d_val;
+			break;
+
+		case DT_RELENT:
+			relent = dyn[i].d_un.d_val;
+			break;
+
+		case DT_RELA:
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	if (!rel && relent == 0)
+		return EFI_SUCCESS;
+
+	if (!rel || relent == 0)
+		return EFI_LOAD_ERROR;
+
+	while (relsz > 0) {
+		/* apply the relocs */
+		switch (ELF32_R_TYPE(rel->r_info)) {
+		case R_386_NONE:
+			break;
+
+		case R_386_RELATIVE:
+			addr = (unsigned long *)(ldbase + rel->r_offset);
+			*addr += ldbase;
+			break;
+
+		default:
+			break;
+		}
+		rel = (Elf32_Rel *)((char *)rel + relent);
+		relsz -= relent;
+	}
+
+	return EFI_SUCCESS;
+}