efi: arm: Add EFI app support

Add support for EFI apps on ARM. This includes start-up and relocation
code, plus a link script and some compiler setting changes.

Signed-off-by: Simon Glass <sjg@chromium.org>
[agraf: Remove whitespace change, add kconfig dep]
Signed-off-by: Alexander Graf <agraf@suse.de>
diff --git a/arch/arm/config.mk b/arch/arm/config.mk
index 542b897..27914a9 100644
--- a/arch/arm/config.mk
+++ b/arch/arm/config.mk
@@ -13,6 +13,9 @@
 endif
 endif
 
+CFLAGS_NON_EFI := -fno-pic -ffixed-r9 -ffunction-sections -fdata-sections
+CFLAGS_EFI := -fpic -fshort-wchar
+
 LDFLAGS_FINAL += --gc-sections
 PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections \
 		     -fno-common -ffixed-r9
@@ -148,3 +151,7 @@
 ALL-y += u-boot.vyb
 endif
 endif
+
+EFI_LDS := elf_arm_efi.lds
+EFI_CRT0 := crt0_arm_efi.o
+EFI_RELOC := reloc_arm_efi.o
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index caa62c6..a812306 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -92,3 +92,13 @@
 AFLAGS_memcpy.o := -DMEMCPY_NO_THUMB_BUILD
 endif
 endif
+
+# For building EFI apps
+CFLAGS_$(EFI_CRT0) := $(CFLAGS_EFI)
+CFLAGS_REMOVE_$(EFI_CRT0) := $(CFLAGS_NON_EFI)
+
+CFLAGS_$(EFI_RELOC) := $(CFLAGS_EFI)
+CFLAGS_REMOVE_$(EFI_RELOC) := $(CFLAGS_NON_EFI)
+
+extra-$(CONFIG_CMD_BOOTEFI_HELLO) += $(EFI_CRT0) $(EFI_RELOC)
+extra-$(CONFIG_EFI) += $(EFI_CRT0) $(EFI_RELOC)
diff --git a/arch/arm/lib/crt0_arm_efi.S b/arch/arm/lib/crt0_arm_efi.S
new file mode 100644
index 0000000..967c885
--- /dev/null
+++ b/arch/arm/lib/crt0_arm_efi.S
@@ -0,0 +1,138 @@
+/*
+ * crt0-efi-arm.S - PE/COFF header for ARM EFI applications
+ *
+ * Copright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+     BSD-2-Clause
+ *
+ * This file is taken and modified from the gnu-efi project.
+ */
+
+	.section	.text.head
+
+	/*
+	 * Magic "MZ" signature for PE/COFF
+	 */
+	.globl	image_base
+image_base:
+	.ascii	"MZ"
+	.skip	58				/* 'MZ' + pad + offset == 64 */
+	.long	pe_header - image_base		/* Offset to the PE header */
+pe_header:
+	.ascii	"PE"
+	.short	0
+coff_header:
+	.short	0x1c2				/* Mixed ARM/Thumb */
+	.short	2				/* nr_sections */
+	.long	0				/* TimeDateStamp */
+	.long	0				/* PointerToSymbolTable */
+	.long	1				/* NumberOfSymbols */
+	.short	section_table - optional_header	/* SizeOfOptionalHeader */
+	/*
+	 * Characteristics: IMAGE_FILE_32BIT_MACHINE |
+	 * IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE |
+	 * IMAGE_FILE_LINE_NUMS_STRIPPED
+	 */
+	.short	0x306
+optional_header:
+	.short	0x10b				/* PE32+ format */
+	.byte	0x02				/* MajorLinkerVersion */
+	.byte	0x14				/* MinorLinkerVersion */
+	.long	_edata - _start			/* SizeOfCode */
+	.long	0				/* SizeOfInitializedData */
+	.long	0				/* SizeOfUninitializedData */
+	.long	_start - image_base		/* AddressOfEntryPoint */
+	.long	_start - image_base		/* BaseOfCode */
+	.long	0				/* BaseOfData */
+
+extra_header_fields:
+	.long	0				/* image_base */
+	.long	0x20				/* SectionAlignment */
+	.long	0x8				/* FileAlignment */
+	.short	0				/* MajorOperatingSystemVersion */
+	.short	0				/* MinorOperatingSystemVersion */
+	.short	0				/* MajorImageVersion */
+	.short	0				/* MinorImageVersion */
+	.short	0				/* MajorSubsystemVersion */
+	.short	0				/* MinorSubsystemVersion */
+	.long	0				/* Win32VersionValue */
+
+	.long	_edata - image_base		/* SizeOfImage */
+
+	/*
+	 * Everything before the kernel image is considered part of the header
+	 */
+	.long	_start - image_base		/* SizeOfHeaders */
+	.long	0				/* CheckSum */
+	.short	EFI_SUBSYSTEM			/* Subsystem */
+	.short	0				/* DllCharacteristics */
+	.long	0				/* SizeOfStackReserve */
+	.long	0				/* SizeOfStackCommit */
+	.long	0				/* SizeOfHeapReserve */
+	.long	0				/* SizeOfHeapCommit */
+	.long	0				/* LoaderFlags */
+	.long	0x6				/* NumberOfRvaAndSizes */
+
+	.quad	0				/* ExportTable */
+	.quad	0				/* ImportTable */
+	.quad	0				/* ResourceTable */
+	.quad	0				/* ExceptionTable */
+	.quad	0				/* CertificationTable */
+	.quad	0				/* BaseRelocationTable */
+
+section_table:
+
+	/*
+	 * The EFI application loader requires a relocation section
+	 * because EFI applications must be relocatable.  This is a
+	 * dummy section as far as we are concerned.
+	 */
+	.ascii	".reloc"
+	.byte	0
+	.byte	0			/* end of 0 padding of section name */
+	.long	0
+	.long	0
+	.long	0			/* SizeOfRawData */
+	.long	0			/* PointerToRawData */
+	.long	0			/* PointerToRelocations */
+	.long	0			/* PointerToLineNumbers */
+	.short	0			/* NumberOfRelocations */
+	.short	0			/* NumberOfLineNumbers */
+	.long	0x42100040		/* Characteristics (section flags) */
+
+	.ascii	".text"
+	.byte	0
+	.byte	0
+	.byte	0			/* end of 0 padding of section name */
+	.long	_edata - _start		/* VirtualSize */
+	.long	_start - image_base	/* VirtualAddress */
+	.long	_edata - _start		/* SizeOfRawData */
+	.long	_start - image_base	/* PointerToRawData */
+
+	.long	0		/* PointerToRelocations (0 for executables) */
+	.long	0		/* PointerToLineNumbers (0 for executables) */
+	.short	0		/* NumberOfRelocations  (0 for executables) */
+	.short	0		/* NumberOfLineNumbers  (0 for executables) */
+	.long	0xe0500020	/* Characteristics (section flags) */
+
+_start:
+	stmfd		sp!, {r0-r2, lr}
+
+	mov		r2, r0
+	mov		r3, r1
+	adr		r1, .L_DYNAMIC
+	ldr		r0, [r1]
+	add		r1, r0, r1
+	adr		r0, image_base
+	bl		_relocate
+	teq		r0, #0
+	bne		0f
+
+	ldmfd		sp, {r0-r1}
+	bl		efi_main
+
+0:	add		sp, sp, #12
+	ldr		pc, [sp], #4
+
+.L_DYNAMIC:
+	.word		_DYNAMIC - .
diff --git a/arch/arm/lib/elf_arm_efi.lds b/arch/arm/lib/elf_arm_efi.lds
new file mode 100644
index 0000000..59f66a1
--- /dev/null
+++ b/arch/arm/lib/elf_arm_efi.lds
@@ -0,0 +1,70 @@
+/*
+ * U-Boot ARM EFI linker script
+ *
+ * SPDX-License-Identifier:	BSD-2-Clause
+ *
+ * Modified from elf_arm_efi.lds in gnu-efi
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+	.text 0x0 : {
+		_text = .;
+		*(.text.head)
+		*(.text)
+		*(.text.*)
+		*(.gnu.linkonce.t.*)
+		*(.srodata)
+		*(.rodata*)
+		. = ALIGN(16);
+	}
+	_etext = .;
+	_text_size = . - _text;
+	.dynamic  : { *(.dynamic) }
+	.data : {
+		_data = .;
+		*(.sdata)
+		*(.data)
+		*(.data1)
+		*(.data.*)
+		*(.got.plt)
+		*(.got)
+
+		/*
+		 * The EFI loader doesn't seem to like a .bss section, so we
+		 * stick it all into .data:
+		 */
+		. = ALIGN(16);
+		_bss = .;
+		*(.sbss)
+		*(.scommon)
+		*(.dynbss)
+		*(.bss)
+		*(.bss.*)
+		*(COMMON)
+		. = ALIGN(16);
+		_bss_end = .;
+		_edata = .;
+	}
+	.rel.dyn : { *(.rel.dyn) }
+	.rel.plt : { *(.rel.plt) }
+	.rel.got : { *(.rel.got) }
+	.rel.data : { *(.rel.data) *(.rel.data*) }
+	_data_size = . - _etext;
+
+	. = ALIGN(4096);
+	.dynsym   : { *(.dynsym) }
+	. = ALIGN(4096);
+	.dynstr   : { *(.dynstr) }
+	. = ALIGN(4096);
+	.note.gnu.build-id : { *(.note.gnu.build-id) }
+	/DISCARD/ : {
+		*(.rel.reloc)
+		*(.eh_frame)
+		*(.note.GNU-stack)
+	}
+	.comment 0 : { *(.comment) }
+}
diff --git a/arch/arm/lib/reloc_arm_efi.c b/arch/arm/lib/reloc_arm_efi.c
new file mode 100644
index 0000000..d2f96ee
--- /dev/null
+++ b/arch/arm/lib/reloc_arm_efi.c
@@ -0,0 +1,66 @@
+/*
+ * reloc_arm.c - position-independent ARM ELF shared object relocator
+ *
+ * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 1999 Hewlett-Packard Co.
+ * Contributed by David Mosberger <davidm@hpl.hp.com>.
+ *
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ *
+ * This file is taken and modified from the gnu-efi project.
+ */
+
+#include <efi.h>
+#include <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;
+	ulong *addr;
+	int i;
+
+	for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
+		switch (dyn[i].d_tag) {
+		case DT_REL:
+			rel = (Elf32_Rel *)((ulong)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;
+		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_ARM_NONE:
+			break;
+		case R_ARM_RELATIVE:
+			addr = (ulong *)(ldbase + rel->r_offset);
+			*addr += ldbase;
+			break;
+		default:
+			break;
+		}
+		rel = (Elf32_Rel *)((char *)rel + relent);
+		relsz -= relent;
+	}
+
+	return EFI_SUCCESS;
+}