linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] arm64: Add EFI stub and runtime services support
@ 2013-11-29 22:05 Mark Salter
       [not found] ` < 1385762712-17043-2-git-send-email-msalter@redhat.com>
                   ` (3 more replies)
  0 siblings, 4 replies; 24+ messages in thread
From: Mark Salter @ 2013-11-29 22:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: patches, Mark Salter, Catalin Marinas, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz, Ard Biesheuvel, linux-doc, Rob Landley

This patch series adds EFI support to the arm64 kernel. This support has
two main parts: an EFI stub and runtime support. The EFI stub support
has the kernel masquerade as a PE/COFF application which can be directly
booted by EFI firmware (or by secondary loaders with EFI support). The
runtime services support provides access to various EFI firmware services
such as reboot, real-time clock, boot variables, and others.

These patches have dependencies on other patches which are not yet in
the kernel but have been posted and are currently under review. In
particular:

  - Generic fixmap support being discussed here:
      http://lkml.org/lkml/2013/11/25/474

  - early_ioremap support being discussed here:
      https://lkml.org/lkml/2013/11/27/621

  - shared EFI update_fdt() function being discussed here:
      https://lkml.org/lkml/2013/11/27/565

A repo with this patch series and the prerequisite patches is at:

  git://github.com/mosalter/linux.git (arm64-efi-patches branch)

Mark Salter (3):
  arm64: add EFI stub
  doc: arm64: add description of EFI stub support
  arm64: add EFI runtime services

 Documentation/arm64/booting.txt |   4 +
 Documentation/efi-stub.txt      |  10 +-
 arch/arm64/Kconfig              |  25 ++
 arch/arm64/include/asm/efi.h    |  18 ++
 arch/arm64/kernel/Makefile      |   4 +
 arch/arm64/kernel/efi-entry.S   |  81 +++++++
 arch/arm64/kernel/efi-stub.c    | 280 ++++++++++++++++++++++
 arch/arm64/kernel/efi.c         | 507 ++++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/head.S        | 112 +++++++++
 arch/arm64/kernel/setup.c       |   6 +
 include/linux/efi.h             |   2 +-
 11 files changed, 1046 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm64/include/asm/efi.h
 create mode 100644 arch/arm64/kernel/efi-entry.S
 create mode 100644 arch/arm64/kernel/efi-stub.c
 create mode 100644 arch/arm64/kernel/efi.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 1/3] arm64: add EFI stub
  2013-11-29 22:05 [PATCH 0/3] arm64: Add EFI stub and runtime services support Mark Salter
       [not found] ` < 1385762712-17043-2-git-send-email-msalter@redhat.com>
@ 2013-11-29 22:05 ` Mark Salter
  2013-12-03 18:38   ` Will Deacon
                     ` (2 more replies)
  2013-11-29 22:05 ` [PATCH 2/3] doc: arm64: add description of EFI stub support Mark Salter
  2013-11-29 22:05 ` [PATCH 3/3] arm64: add EFI runtime services Mark Salter
  3 siblings, 3 replies; 24+ messages in thread
From: Mark Salter @ 2013-11-29 22:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: patches, Mark Salter, Ard Biesheuvel, Catalin Marinas,
	Will Deacon, linux-arm-kernel, matt.fleming, linux-efi,
	Leif Lindholm, roy.franz

This patch adds PE/COFF header fields to the start of the Image
so that it appears as an EFI application to EFI firmware. An EFI
stub is included to allow direct booting of the kernel Image. Due
to EFI firmware limitations, only little endian kernels with 4K
page sizes are supported at this time. Support in the COFF header
for signed images was provided by Ard Biesheuvel.

Signed-off-by: Mark Salter <msalter@redhat.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
CC: Catalin Marinas <catalin.marinas@arm.com>
CC: Will Deacon <will.deacon@arm.com>
CC: linux-arm-kernel@lists.infradead.org
CC: matt.fleming@intel.com
CC: linux-efi@vger.kernel.org
CC: Leif Lindholm <leif.lindholm@linaro.org>
CC: roy.franz@linaro.org
---
 arch/arm64/Kconfig            |  10 ++
 arch/arm64/kernel/Makefile    |   3 +
 arch/arm64/kernel/efi-entry.S |  81 ++++++++++++
 arch/arm64/kernel/efi-stub.c  | 280 ++++++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/head.S      | 112 +++++++++++++++++
 5 files changed, 486 insertions(+)
 create mode 100644 arch/arm64/kernel/efi-entry.S
 create mode 100644 arch/arm64/kernel/efi-stub.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 809c1b8..10b0e93 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -250,6 +250,16 @@ config CMDLINE_FORCE
 	  This is useful if you cannot or don't want to change the
 	  command-line options your boot loader passes to the kernel.
 
+config EFI_STUB
+	bool "EFI stub support"
+	depends on !CPU_BIG_ENDIAN && !ARM64_64K_PAGES && OF
+	select LIBFDT
+	default y
+	help
+	  This kernel feature allows an Image to be loaded directly
+	  by EFI firmware without the use of a bootloader.
+	  See Documentation/efi-stub.txt for more information.
+
 endmenu
 
 menu "Userspace binary formats"
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 5ba2fd4..1c52b84 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -4,6 +4,8 @@
 
 CPPFLAGS_vmlinux.lds	:= -DTEXT_OFFSET=$(TEXT_OFFSET)
 AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_efi-stub.o 	:= -DTEXT_OFFSET=$(TEXT_OFFSET) \
+			   -I$(src)/../../../scripts/dtc/libfdt
 
 # Object file lists.
 arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\
@@ -18,6 +20,7 @@ arm64-obj-$(CONFIG_SMP)			+= smp.o smp_spin_table.o
 arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
 arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
 arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
+arm64-obj-$(CONFIG_EFI_STUB)		+= efi-stub.o efi-entry.o
 
 obj-y					+= $(arm64-obj-y) vdso/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S
new file mode 100644
index 0000000..5f6d179
--- /dev/null
+++ b/arch/arm64/kernel/efi-entry.S
@@ -0,0 +1,81 @@
+/*
+ * EFI entry point.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * Author: Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+
+#define EFI_LOAD_ERROR 0x8000000000000001
+
+	__INIT
+
+	/*
+	 * We arrive here from the EFI boot manager with:
+	 *
+	 *    * MMU on with identity-mapped RAM.
+	 *    * Icache and Dcache on
+	 *
+	 * We will most likely be running from some place other than where
+	 * we want to be. The kernel image wants to be placed at TEXT_OFFSET
+	 * from start of RAM.
+	 */
+ENTRY(efi_stub_entry)
+	stp	x29, x30, [sp, #-32]!
+
+	/*
+	 * Call efi_entry to do the real work.
+	 * x0 and x1 are already set up by firmware. Current runtime
+	 * address of image is calculated and passed via *image_addr.
+	 *
+	 * unsigned long efi_entry(void *handle,
+	 *                         efi_system_table_t *sys_table,
+	 *                         unsigned long *image_addr) ;
+	 */
+	adrp	x8, _text
+        add	x8, x8, #:lo12:_text
+	add	x2, sp, 16
+	str	x8, [x2]
+	bl	efi_entry
+	cmn	x0, #1
+	b.eq	efi_load_fail
+
+	/*
+	 * efi_entry() will have relocated the kernel image if necessary
+	 * and we return here with device tree address in x0 and the kernel
+	 * entry point stored at *image_addr. Save those values in registers
+	 * which are preserved by __flush_dcache_all.
+	 */
+	ldr	x1, [sp, #16]
+	mov	x20, x0
+	mov	x21, x1
+
+	bl	__flush_dcache_all
+	/* Turn off Dcache and MMU */
+	mrs	x0, sctlr_el1
+	bic	x0, x0, #1 << 0	// clear SCTLR.M
+	bic	x0, x0, #1 << 2	// clear SCTLR.C
+	msr	sctlr_el1, x0
+	isb
+
+	/* Jump to real entry point */
+	mov	x0, x20
+	mov	x1, xzr
+	mov	x2, xzr
+	mov	x3, xzr
+	br	x21
+
+efi_load_fail:
+	mov	x0, EFI_LOAD_ERROR
+	ldp	x29, x30, [sp], #32
+	ret
+
+ENDPROC(efi_stub_entry)
diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c
new file mode 100644
index 0000000..f000b04
--- /dev/null
+++ b/arch/arm64/kernel/efi-stub.c
@@ -0,0 +1,280 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
+ *
+ * This file implements the EFI boot stub for the arm64 kernel.
+ * Adapted from ARM version by Mark Salter <msalter@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/efi.h>
+#include <linux/libfdt.h>
+#include <asm/sections.h>
+#include <generated/compile.h>
+#include <linux/uts.h>
+#include <linux/utsname.h>
+#include <generated/utsrelease.h>
+#include <linux/version.h>
+
+/* error code which can't be mistaken for valid address */
+#define EFI_ERROR	(~0UL)
+
+/*
+ * EFI function call wrappers. These are not required for arm64, but wrappers
+ * are required for X86 to convert between ABIs. These wrappers are provided
+ * to allow code sharing between X86 and other architectures. Since these
+ * wrappers directly invoke the EFI function pointer, the function pointer
+ * type must be properly defined, which is not the case for X86. One advantage
+ * of this is it allows for type checking of arguments, which is not possible
+ * with the X86 wrappers.
+ */
+#define efi_call_phys0(f)			f()
+#define efi_call_phys1(f, a1)			f(a1)
+#define efi_call_phys2(f, a1, a2)		f(a1, a2)
+#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
+#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
+#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
+
+/*
+ * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
+ * start of kernel and may not cross a 2MiB boundary. We set alignment to
+ * equal max size so we know it won't cross a 2MiB boudary.
+ */
+#define MAX_DTB_SIZE	0x40000
+#define DTB_ALIGN	MAX_DTB_SIZE
+#define MAX_DTB_OFFSET	0x20000000
+
+#define pr_efi(msg)     efi_printk(sys_table, "EFI stub: "msg)
+#define pr_efi_err(msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
+
+struct fdt_region {
+	u64 base;
+	u64 size;
+};
+
+/* Include shared EFI stub code */
+#include "../../../drivers/firmware/efi/efi-stub-helper.c"
+#include "../../../drivers/firmware/efi/fdt.c"
+
+static unsigned long __init get_dram_base(efi_system_table_t *sys_table)
+{
+	efi_status_t status;
+	unsigned long map_size, desc_size;
+	unsigned long membase = EFI_ERROR;
+	efi_memory_desc_t *memory_map;
+	int i;
+
+	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
+				    &desc_size, NULL, NULL);
+	if (status == EFI_SUCCESS) {
+		for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
+			efi_memory_desc_t *desc;
+			unsigned long m = (unsigned long)memory_map;
+
+			desc = (efi_memory_desc_t *)(m + (i * desc_size));
+
+			if (desc->num_pages == 0)
+				break;
+
+			if (desc->type == EFI_CONVENTIONAL_MEMORY) {
+				unsigned long base = desc->phys_addr;
+
+				base &= ~((unsigned long)(TEXT_OFFSET - 1));
+
+				if (membase > base)
+					membase = base;
+			}
+		}
+	}
+	return membase;
+}
+
+unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table,
+			       unsigned long *image_addr)
+{
+	efi_loaded_image_t *image;
+	efi_status_t status;
+	unsigned long image_size, image_memsize = 0;
+	unsigned long dram_base;
+	/* addr/point and size pairs for memory management*/
+	u64 initrd_addr;
+	u64 initrd_size = 0;
+	u64 fdt_addr;  /* Original DTB */
+	u64 fdt_size = 0;
+	unsigned long new_fdt_size;
+	char *cmdline_ptr;
+	int cmdline_size = 0;
+	unsigned long new_fdt_addr;
+	unsigned long map_size, desc_size;
+	unsigned long mmap_key;
+	efi_memory_desc_t *memory_map;
+	u32 desc_ver;
+	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	pr_efi("Booting Linux Kernel...\n");
+
+	/* get the command line from EFI, using the LOADED_IMAGE protocol */
+	status = efi_call_phys3(sys_table->boottime->handle_protocol,
+				handle, &proto, (void *)&image);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		goto fail;
+	}
+
+	/*
+	 * We are going to copy this into device tree, so we don't care where
+	 * in memory it is.
+	 */
+	cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
+						   &cmdline_size);
+	if (!cmdline_ptr) {
+		pr_efi_err("Failed to convert command line to ascii\n");
+		goto fail;
+	}
+
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
+				      ~0UL, (unsigned long *)&fdt_addr,
+				      (unsigned long *)&fdt_size);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to load device tree blob\n");
+		goto fail_free_cmdline;
+	}
+
+	if (fdt_check_header((void *)fdt_addr)) {
+		pr_efi_err("Device Tree header not valid\n");
+		goto fail_free_dtb;
+	}
+	if (fdt_totalsize((void *)fdt_addr) > fdt_size) {
+		pr_efi_err("Incomplete device tree\n");
+		goto fail_free_dtb;
+	}
+
+	dram_base = get_dram_base(sys_table);
+	if (dram_base == EFI_ERROR) {
+		pr_efi_err("Failed to get DRAM base\n");
+		goto fail_free_dtb;
+	}
+
+	/* Relocate the image, if required. */
+	image_size = image->image_size;
+	if (*image_addr != (dram_base + TEXT_OFFSET)) {
+		image_memsize = image_size + (_end - _edata);
+		status = efi_relocate_kernel(sys_table, image_addr,
+					     image_size, image_memsize,
+					     dram_base + TEXT_OFFSET,
+					     PAGE_SIZE);
+		if (status != EFI_SUCCESS) {
+			pr_efi_err("Failed to relocate kernel\n");
+			goto fail_free_dtb;
+		}
+		if (*image_addr != (dram_base + TEXT_OFFSET)) {
+			pr_efi_err("Failed to alloc kernel memory\n");
+			goto fail_free_image;
+		}
+	}
+
+	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
+				      dram_base + 0x20000000,
+				      (unsigned long *)&initrd_addr,
+				      (unsigned long *)&initrd_size);
+	if (status != EFI_SUCCESS)
+		pr_efi("No initrd found\n");
+
+	/*
+	 * Estimate size of new FDT, and allocate memory for it. We
+	 * will allocate a bigger buffer if this ends up being too
+	 * small, so a rough guess is OK here. We increment the size
+	 * by PAGE_SIZE since the firmware allocates by pages anyway.
+	 */
+	new_fdt_size = fdt_size + EFI_PAGE_SIZE;
+	while (1) {
+		status = efi_high_alloc(sys_table, new_fdt_size, DTB_ALIGN,
+					&new_fdt_addr,
+					dram_base + MAX_DTB_OFFSET);
+		if (status != EFI_SUCCESS) {
+			pr_efi_err("No memory for new device tree\n");
+			goto fail_free_initrd;
+		}
+
+		/*
+		 * Now that we have done our final memory allocation, we can
+		 * get the memory map key needed for exit_boot_services().
+		 */
+		status = efi_get_memory_map(sys_table, &memory_map, &map_size,
+					    &desc_size, &desc_ver, &mmap_key);
+		if (status != EFI_SUCCESS)
+			goto fail_free_new_fdt;
+
+		status = update_fdt(sys_table,
+				    (void *)fdt_addr, (void *)new_fdt_addr,
+				    new_fdt_size, cmdline_ptr,
+				    initrd_addr, initrd_size,
+				    memory_map, map_size, desc_size, desc_ver);
+
+		/* Succeeding the first time is the expected case. */
+		if (status == EFI_SUCCESS)
+			break;
+
+		if (status == EFI_BUFFER_TOO_SMALL) {
+			/*
+			 * We need to allocate more space for the new
+			 * device tree, so free existing buffer that is
+			 * too small.  Also free memory map, as we will need
+			 * to get new one that reflects the free/alloc we do
+			 * on the device tree buffer.
+			 */
+			efi_free(sys_table, new_fdt_size, new_fdt_addr);
+			efi_call_phys1(sys_table->boottime->free_pool,
+				       memory_map);
+			new_fdt_size += EFI_PAGE_SIZE;
+		} else {
+			pr_efi_err("Unable to constuct new device tree\n");
+			goto fail_free_mmap;
+		}
+	}
+
+	/* Now we are ready to exit_boot_services.*/
+	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
+				handle, mmap_key);
+
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Exit boot services failed\n");
+		goto fail_free_mmap;
+	}
+
+	/*
+	 * Now we need to return the FDT address to the calling
+	 * function so it can be used as part of normal boot.
+	 */
+	return new_fdt_addr;
+
+fail_free_mmap:
+	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
+
+fail_free_new_fdt:
+	efi_free(sys_table, new_fdt_size, new_fdt_addr);
+
+fail_free_initrd:
+	efi_free(sys_table, initrd_size, initrd_addr);
+
+fail_free_image:
+	efi_free(sys_table, image_memsize, *image_addr);
+
+fail_free_dtb:
+	if (fdt_addr)
+		efi_free(sys_table, fdt_size, fdt_addr);
+
+fail_free_cmdline:
+	efi_free(sys_table, cmdline_size, (u64)cmdline_ptr);
+
+fail:
+	return EFI_ERROR;
+}
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 03adf8f..720429e 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -107,8 +107,18 @@
 	/*
 	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
 	 */
+#ifdef CONFIG_EFI_STUB
+	/*
+	 * Magic "MZ" signature for PE/COFF
+	 * Little Endian:  add x13, x18, #0x16
+	 */
+efi_head:
+	.long   0x91005a4d
+	b	stext
+#else
 	b	stext				// branch to kernel start, magic
 	.long	0				// reserved
+#endif
 	.quad	TEXT_OFFSET			// Image load offset from start of RAM
 	.quad	0				// reserved
 	.quad	0				// reserved
@@ -119,7 +129,109 @@
 	.byte	0x52
 	.byte	0x4d
 	.byte	0x64
+#ifdef CONFIG_EFI_STUB
+	.long	pe_header - efi_head		// Offset to the PE header.
+#else
 	.word	0				// reserved
+#endif
+
+#ifdef CONFIG_EFI_STUB
+	.align 3
+pe_header:
+	.ascii	"PE"
+	.short 	0
+coff_header:
+	.short	0xaa64				// AArch64
+	.short	2				// nr_sections
+	.long	0 				// TimeDateStamp
+	.long	0				// PointerToSymbolTable
+	.long	1				// NumberOfSymbols
+	.short	section_table - optional_header	// SizeOfOptionalHeader
+	.short	0x206				// Characteristics.
+						// IMAGE_FILE_DEBUG_STRIPPED |
+						// IMAGE_FILE_EXECUTABLE_IMAGE |
+						// IMAGE_FILE_LINE_NUMS_STRIPPED
+optional_header:
+	.short	0x20b				// PE32+ format
+	.byte	0x02				// MajorLinkerVersion
+	.byte	0x14				// MinorLinkerVersion
+	.long	_edata - stext			// SizeOfCode
+	.long	0				// SizeOfInitializedData
+	.long	0				// SizeOfUninitializedData
+	.long	efi_stub_entry - efi_head	// AddressOfEntryPoint
+	.long	stext - efi_head		// BaseOfCode
+
+extra_header_fields:
+	.quad	0				// ImageBase
+	.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 - efi_head		// SizeOfImage
+
+	// Everything before the kernel image is considered part of the header
+	.long	stext - efi_head			// SizeOfHeaders
+	.long	0				// CheckSum
+	.short	0xa				// Subsystem (EFI application)
+	.short	0				// DllCharacteristics
+	.quad	0				// SizeOfStackReserve
+	.quad	0				// SizeOfStackCommit
+	.quad	0				// SizeOfHeapReserve
+	.quad	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
+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 - stext		// VirtualSize
+	.long	stext - efi_head	// VirtualAddress
+	.long	_edata - stext		// SizeOfRawData
+	.long	stext - efi_head	// 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)
+	.align 5
+#endif
 
 ENTRY(stext)
 	mov	x21, x0				// x21=FDT
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 2/3] doc: arm64: add description of EFI stub support
  2013-11-29 22:05 [PATCH 0/3] arm64: Add EFI stub and runtime services support Mark Salter
       [not found] ` < 1385762712-17043-2-git-send-email-msalter@redhat.com>
  2013-11-29 22:05 ` [PATCH 1/3] arm64: add EFI stub Mark Salter
@ 2013-11-29 22:05 ` Mark Salter
  2013-12-05 12:53   ` Grant Likely
  2013-11-29 22:05 ` [PATCH 3/3] arm64: add EFI runtime services Mark Salter
  3 siblings, 1 reply; 24+ messages in thread
From: Mark Salter @ 2013-11-29 22:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: patches, Mark Salter, Catalin Marinas, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, linux-doc,
	Rob Landley, Leif Lindholm, roy.franz

Signed-off-by: Mark Salter <msalter@redhat.com>
CC: Catalin Marinas <catalin.marinas@arm.com>
CC: Will Deacon <will.deacon@arm.com>
CC: linux-arm-kernel@lists.infradead.org
CC: matt.fleming@intel.com
CC: linux-efi@vger.kernel.org
CC: linux-doc@vger.kernel.org
CC: Rob Landley <rob@landley.net> 
CC: Leif Lindholm <leif.lindholm@linaro.org>
CC: roy.franz@linaro.org
---
 Documentation/arm64/booting.txt |  4 ++++
 Documentation/efi-stub.txt      | 10 ++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/Documentation/arm64/booting.txt b/Documentation/arm64/booting.txt
index a9691cc..aa95d38c 100644
--- a/Documentation/arm64/booting.txt
+++ b/Documentation/arm64/booting.txt
@@ -85,6 +85,10 @@ The decompressed kernel image contains a 64-byte header as follows:
 Header notes:
 
 - code0/code1 are responsible for branching to stext.
+- when booting through EFI, code0/code1 are initially skipped.
+  res5 is an offset to the PE header and the PE header has the EFI
+  entry point (efi_stub_entry). When the stub has done its work, it
+  jumps to code0 to resume the normal boot process.
 
 The image must be placed at the specified offset (currently 0x80000)
 from the start of the system RAM and called there. The start of the
diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
index 19e897c..c2a4b11 100644
--- a/Documentation/efi-stub.txt
+++ b/Documentation/efi-stub.txt
@@ -12,6 +12,11 @@ arch/arm/boot/compressed/efi-header.S and
 arch/arm/boot/compressed/efi-stub.c.  EFI stub code that is shared
 between architectures is in drivers/firmware/efi/efi-stub-helper.c.
 
+For arm64, there is no compressed kernel support, so the Image itself
+masquerades as a PE/COFF image and the EFI stub is linked into the
+kernel. The arm64 EFI stub lives in arch/arm64/kernel/efi-entry.S
+and arch/arm64/kernel/efi-stub.c.
+
 By using the EFI boot stub it's possible to boot a Linux kernel
 without the use of a conventional EFI boot loader, such as grub or
 elilo. Since the EFI boot stub performs the jobs of a boot loader, in
@@ -28,7 +33,8 @@ the extension the EFI firmware loader will refuse to execute it. It's
 not possible to execute bzImage.efi from the usual Linux file systems
 because EFI firmware doesn't have support for them.  For ARM the
 arch/arm/boot/zImage should be copied to the system partition, and it
-may not need to be renamed.
+may not need to be renamed. Similarly for arm64, arch/arm64/boot/Image
+should be copied but not necessarily renamed.
 
 
 **** Passing kernel parameters from the EFI shell
@@ -72,7 +78,7 @@ is passed to bzImage.efi.
 
 **** The "dtb=" option
 
-For the ARM architecture, we also need to be able to provide a device
+For ARM and arm64 architecture, we also need to be able to provide a device
 tree to the kernel.  This is done with the "dtb=" command line option,
 and is process in the same manner as the "initrd=" option that is described
 above.
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 3/3] arm64: add EFI runtime services
  2013-11-29 22:05 [PATCH 0/3] arm64: Add EFI stub and runtime services support Mark Salter
                   ` (2 preceding siblings ...)
  2013-11-29 22:05 ` [PATCH 2/3] doc: arm64: add description of EFI stub support Mark Salter
@ 2013-11-29 22:05 ` Mark Salter
  2013-12-05 15:25   ` Catalin Marinas
  2013-12-09 13:52   ` Leif Lindholm
  3 siblings, 2 replies; 24+ messages in thread
From: Mark Salter @ 2013-11-29 22:05 UTC (permalink / raw)
  To: linux-kernel
  Cc: patches, Mark Salter, Catalin Marinas, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

This patch adds EFI runtime support for arm64. The runtime support allows the
kernel to access various EFI runtime services provided by EFI firmware. Things
like reboot, real time clock, EFI boot variables, and others.

Signed-off-by: Mark Salter <msalter@redhat.com>
CC: Catalin Marinas <catalin.marinas@arm.com>
CC: Will Deacon <will.deacon@arm.com>
CC: linux-arm-kernel@lists.infradead.org
CC: matt.fleming@intel.com
CC: linux-efi@vger.kernel.org
CC: Leif Lindholm <leif.lindholm@linaro.org>
CC: roy.franz@linaro.org
---
 arch/arm64/Kconfig           |  15 ++
 arch/arm64/include/asm/efi.h |  18 ++
 arch/arm64/kernel/Makefile   |   1 +
 arch/arm64/kernel/efi.c      | 507 +++++++++++++++++++++++++++++++++++++++++++
 arch/arm64/kernel/setup.c    |   6 +
 include/linux/efi.h          |   2 +-
 6 files changed, 548 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm64/include/asm/efi.h
 create mode 100644 arch/arm64/kernel/efi.c

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 10b0e93..6baaf75 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -250,6 +250,19 @@ config CMDLINE_FORCE
 	  This is useful if you cannot or don't want to change the
 	  command-line options your boot loader passes to the kernel.
 
+config EFI
+        bool "EFI runtime service support"
+	depends on !CPU_BIG_ENDIAN && !ARM64_64K_PAGES && OF
+	select UCS2_STRING
+	select LIBFDT
+	help
+	  This enables the kernel to use UEFI runtime services that are
+	  available (such as the UEFI variable services).
+
+	  This option is only useful on systems that have UEFI firmware.
+	  However, even with this option, the resultant kernel should
+	  continue to boot on existing non-UEFI platforms.
+
 config EFI_STUB
 	bool "EFI stub support"
 	depends on !CPU_BIG_ENDIAN && !ARM64_64K_PAGES && OF
@@ -291,6 +304,8 @@ source "net/Kconfig"
 
 source "drivers/Kconfig"
 
+source "drivers/firmware/Kconfig"
+
 source "fs/Kconfig"
 
 source "arch/arm64/kvm/Kconfig"
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
new file mode 100644
index 0000000..7384048
--- /dev/null
+++ b/arch/arm64/include/asm/efi.h
@@ -0,0 +1,18 @@
+#ifndef _ASM_ARM64_EFI_H
+#define _ASM_ARM64_EFI_H
+
+#include <asm/io.h>
+
+#ifdef CONFIG_EFI
+extern void efi_init(void);
+#else
+#define efi_init()
+#endif
+
+#define efi_remap(cookie, size) __ioremap((cookie), (size), PAGE_KERNEL_EXEC)
+#define efi_ioremap(cookie, size) __ioremap((cookie), (size), \
+					    __pgprot(PROT_DEVICE_nGnRE))
+#define efi_unmap(cookie) __iounmap((cookie))
+#define efi_iounmap(cookie) __iounmap((cookie))
+
+#endif /* _ASM_ARM64_EFI_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1c52b84..8897cf5a5 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -21,6 +21,7 @@ arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
 arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
 arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 arm64-obj-$(CONFIG_EFI_STUB)		+= efi-stub.o efi-entry.o
+arm64-obj-$(CONFIG_EFI)			+= efi.o
 
 obj-y					+= $(arm64-obj-y) vdso/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
new file mode 100644
index 0000000..1bad8a7
--- /dev/null
+++ b/arch/arm64/kernel/efi.c
@@ -0,0 +1,507 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.3.1
+ *
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * Adapted for arm64 from arch/arm/kernel/efi.c code
+ */
+
+#include <linux/efi.h>
+#include <linux/export.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/bootmem.h>
+
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
+
+#define efi_early_remap(a, b) \
+	((__force void *)early_ioremap((a), (b)))
+#define efi_early_unmap(a, b) \
+	early_iounmap((void __iomem *)(a), (b))
+
+struct efi_memory_map memmap;
+
+static efi_runtime_services_t *runtime;
+
+static u64 efi_system_table;
+
+static unsigned long arm_efi_facility;
+
+/*
+ * Returns 1 if 'facility' is enabled, 0 otherwise.
+ */
+int efi_enabled(int facility)
+{
+	return test_bit(facility, &arm_efi_facility) != 0;
+}
+EXPORT_SYMBOL(efi_enabled);
+
+static int uefi_debug __initdata;
+static int __init uefi_debug_setup(char *str)
+{
+	uefi_debug = 1;
+
+	return 0;
+}
+early_param("uefi_debug", uefi_debug_setup);
+
+static int __init fdt_find_efi_params(unsigned long node, const char *uname,
+				      int depth, void *data)
+{
+	unsigned long len, size;
+	__be32 *prop;
+
+	if (depth != 1 ||
+	    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
+		return 0;
+
+	pr_info("Getting EFI parameters from FDT.\n");
+
+	prop = of_get_flat_dt_prop(node, "linux,uefi-system-table", &len);
+	if (!prop) {
+		pr_err("No EFI system table in FDT\n");
+		return 0;
+	}
+	efi_system_table = of_read_ulong(prop, len/4);
+
+	prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-start", &len);
+	if (!prop) {
+		pr_err("No EFI memmap in FDT\n");
+		return 0;
+	}
+	memmap.phys_map = (void *)of_read_ulong(prop, len/4);
+
+	prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-size", &len);
+	if (!prop) {
+		pr_err("No EFI memmap size in FDT\n");
+		return 0;
+	}
+	size = of_read_ulong(prop, len/4);
+	memmap.map_end = memmap.phys_map + size;
+
+	/* reserve memmap */
+	memblock_reserve((u64)memmap.phys_map, size);
+
+	prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-size", &len);
+	if (!prop) {
+		pr_err("No EFI memmap descriptor size in FDT\n");
+		return 0;
+	}
+	memmap.desc_size = of_read_ulong(prop, len/4);
+
+	memmap.nr_map = size / memmap.desc_size;
+
+	prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-ver", &len);
+	if (!prop) {
+		pr_err("No EFI memmap descriptor version in FDT\n");
+		return 0;
+	}
+	memmap.desc_version = of_read_ulong(prop, len/4);
+
+	if (uefi_debug) {
+		pr_info("  EFI system table @ %p\n", (void *)efi_system_table);
+		pr_info("  EFI mmap @ %p-%p\n", memmap.phys_map,
+			memmap.map_end);
+		pr_info("  EFI mmap descriptor size = 0x%lx\n",
+			memmap.desc_size);
+		pr_info("  EFI mmap descriptor version = 0x%lx\n",
+			memmap.desc_version);
+	}
+
+	return 1;
+}
+
+#define PGD_END  (&idmap_pg_dir[sizeof(idmap_pg_dir)/sizeof(pgd_t)])
+#ifndef CONFIG_SMP
+#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF)
+#else
+#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF | \
+		    PMD_SECT_S)
+#endif
+
+static void __init create_idmap(unsigned long addr, unsigned long len)
+{
+	unsigned long end, next, p;
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+
+	/* section align it */
+	len = ALIGN(len + (addr & ~SECTION_MASK), SECTION_SIZE);
+	addr &= SECTION_MASK;
+
+	end = addr + len;
+	pgd = &idmap_pg_dir[pgd_index(addr)];
+
+	do {
+		next = pgd_addr_end(addr, end);
+		if (pgd >= PGD_END)
+			continue;
+
+		pud = pud_offset(pgd, addr);
+		if (pud_none(*pud)) {
+			pmd = alloc_bootmem_pages(PAGE_SIZE);
+			if (!pmd)
+				continue;
+			set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE));
+		}
+
+		for (p = addr; p < next; p += SECTION_SIZE) {
+			pmd = pmd_offset(pud, p);
+
+			if (pmd_none(*pmd))
+				set_pmd(pmd, __pmd(p | PMD_FLAGS));
+		}
+	} while (pgd++, addr = next, addr != end);
+}
+
+static void __init efi_setup_idmap(void)
+{
+	struct memblock_region *r;
+
+	for_each_memblock(memory, r)
+		create_idmap(r->base, r->size);
+}
+
+static int __init uefi_init(void)
+{
+	efi_char16_t *c16;
+	char vendor[100] = "unknown";
+	int i, retval;
+
+	efi.systab = efi_early_remap(efi_system_table,
+				     sizeof(efi_system_table_t));
+
+	/*
+	 * Verify the EFI Table
+	 */
+	if (efi.systab == NULL)
+		panic("Whoa! Can't map EFI system table.\n");
+	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		panic("Whoa! EFI system table signature incorrect\n");
+	if ((efi.systab->hdr.revision >> 16) == 0)
+		pr_warn("Warning: EFI system table version %d.%02d, expected 1.00 or greater\n",
+			efi.systab->hdr.revision >> 16,
+			efi.systab->hdr.revision & 0xffff);
+
+	/* Show what we know for posterity */
+	c16 = efi_early_remap(efi.systab->fw_vendor,
+			      sizeof(vendor));
+	if (c16) {
+		for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
+			vendor[i] = c16[i];
+		vendor[i] = '\0';
+	}
+
+	pr_info("EFI v%u.%.02u by %s\n",
+		efi.systab->hdr.revision >> 16,
+		efi.systab->hdr.revision & 0xffff, vendor);
+
+	retval = efi_config_init(NULL);
+	if (retval == 0)
+		set_bit(EFI_CONFIG_TABLES, &arm_efi_facility);
+
+	efi_early_unmap(c16, sizeof(vendor));
+	efi_early_unmap(efi.systab,  sizeof(efi_system_table_t));
+
+	return retval;
+}
+
+static __initdata struct {
+	u32 type;
+	const char *name;
+}  memory_type_name_map[] = {
+	{EFI_RESERVED_TYPE, "Reserved"},
+	{EFI_LOADER_CODE, "Loader Code"},
+	{EFI_LOADER_DATA, "Loader Data"},
+	{EFI_BOOT_SERVICES_CODE, "Boot Code"},
+	{EFI_BOOT_SERVICES_DATA, "Boot Data"},
+	{EFI_RUNTIME_SERVICES_CODE, "Runtime Code"},
+	{EFI_RUNTIME_SERVICES_DATA, "Runtime Data"},
+	{EFI_CONVENTIONAL_MEMORY, "Conventional Memory"},
+	{EFI_UNUSABLE_MEMORY, "Unusable Memory"},
+	{EFI_ACPI_RECLAIM_MEMORY, "ACPI Reclaim Memory"},
+	{EFI_ACPI_MEMORY_NVS, "ACPI Memory NVS"},
+	{EFI_MEMORY_MAPPED_IO, "Memory Mapped I/O"},
+	{EFI_MEMORY_MAPPED_IO_PORT_SPACE, "MMIO Port Space"},
+	{EFI_PAL_CODE, "PAL Code"},
+	{EFI_MAX_MEMORY_TYPE, NULL},
+};
+
+/* memmap walk functions may return one of these or a negative error */
+#define MEMMAP_WALK_CONT 0
+#define MEMMAP_WALK_STOP 1
+
+static __init int memmap_walk(struct efi_memory_map *memmap,
+			      int (*func)(efi_memory_desc_t *, void *),
+			      void *private_data, int early)
+{
+	void *start, *map;
+	unsigned long len;
+	int i, ret;
+
+	if (early) {
+		len = memmap->map_end - memmap->phys_map;
+		map = start = efi_early_remap((u64)memmap->phys_map, len);
+		if (map == NULL)
+			panic("Can't map EFI memory map.\n");
+
+	} else {
+		len = memmap->map_end - memmap->map;
+		map = memmap->map;
+	}
+
+	for (i = 0; i < memmap->nr_map; i++, map += memmap->desc_size) {
+		ret = func(map, private_data);
+		if (ret)
+			break;
+	}
+
+	if (early)
+		efi_early_unmap(start, len);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static __init int reserve_region(efi_memory_desc_t *md, void *priv)
+{
+	u64 paddr, npages;
+	int *count = priv;
+
+	paddr = md->phys_addr;
+	npages = md->num_pages;
+	memrange_efi_to_native(&paddr, &npages);
+
+	if (uefi_debug)
+		pr_info("  0x%012llx-0x%012llx [%s]",
+			paddr, paddr + (npages << PAGE_SHIFT) - 1,
+			memory_type_name_map[md->type].name);
+
+	if ((md->attribute & EFI_MEMORY_RUNTIME) ||
+	    md->type == EFI_BOOT_SERVICES_CODE ||
+	    md->type == EFI_BOOT_SERVICES_DATA ||
+	    md->type == EFI_ACPI_RECLAIM_MEMORY) {
+		if (md->type != EFI_MEMORY_MAPPED_IO) {
+			memblock_reserve(paddr, npages << PAGE_SHIFT);
+			if (uefi_debug)
+				pr_cont("*");
+			*count += 1;
+		}
+	}
+
+	if (uefi_debug)
+		pr_cont("\n");
+
+	return MEMMAP_WALK_CONT;
+}
+
+static __init void reserve_regions(void)
+{
+	int nr_reserved = 0;
+
+	if (uefi_debug)
+		pr_info("Processing EFI memory map:\n");
+
+	memmap_walk(&memmap, reserve_region, &nr_reserved, 1);
+
+	if (uefi_debug)
+		pr_info("%d EFI regions reserved.\n", nr_reserved);
+}
+
+void __init efi_init(void)
+{
+	/* Grab EFI information from FDT */
+	if (!of_scan_flat_dt(fdt_find_efi_params, NULL))
+		return;
+
+	set_bit(EFI_BOOT, &arm_efi_facility);
+	set_bit(EFI_64BIT, &arm_efi_facility);
+
+	uefi_init();
+
+	reserve_regions();
+}
+
+static int __init remap_region(efi_memory_desc_t *md, void *priv)
+{
+	efi_memory_desc_t *new;
+	u64 paddr, npages, size;
+
+	if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+	    md->type != EFI_ACPI_RECLAIM_MEMORY)
+		return MEMMAP_WALK_CONT;
+
+	paddr = md->phys_addr;
+	npages = md->num_pages;
+	memrange_efi_to_native(&paddr, &npages);
+	size = npages << PAGE_SHIFT;
+
+	/*
+	 * Map everything writeback-capable as coherent memory,
+	 * anything else as device.
+	 */
+	if (md->attribute & EFI_MEMORY_WB) {
+		if (memblock_is_memory(paddr))
+			md->virt_addr = (u64)phys_to_virt(paddr);
+		else
+			md->virt_addr = (__force u64)efi_remap(paddr, size);
+	} else
+		md->virt_addr = (__force u64)efi_ioremap(paddr, size);
+
+	if (!md->virt_addr)
+		panic("EFI unable to remap 0x%llx pages @ %p\n",
+		      npages, (void *)paddr);
+
+	if (uefi_debug)
+		pr_info("  EFI remap 0x%012llx => %p\n",
+			md->phys_addr, (void *)md->virt_addr);
+
+	new = *(efi_memory_desc_t **)priv;
+	*new = *md;
+	*(efi_memory_desc_t **)priv = (void *)new + memmap.desc_size;
+
+	return MEMMAP_WALK_CONT;
+}
+
+static int __init remap_regions(efi_memory_desc_t *map)
+{
+	efi_memory_desc_t *new = map;
+
+	memmap_walk(&memmap, remap_region, &new, 0);
+
+	return new - map;
+}
+
+static int __init free_boot_region(efi_memory_desc_t *md, void *priv)
+{
+	u64 *total = priv;
+	u64 paddr, npages, size;
+
+	if (md->type != EFI_BOOT_SERVICES_CODE &&
+	    md->type != EFI_BOOT_SERVICES_DATA)
+		return MEMMAP_WALK_CONT;
+
+	paddr = md->phys_addr;
+	npages = md->num_pages;
+	memrange_efi_to_native(&paddr, &npages);
+	size = npages << PAGE_SHIFT;
+
+	if (uefi_debug)
+		pr_info("  EFI freeing: 0x%012llx-0x%012llx [%s]\n",
+			paddr, paddr + (npages << PAGE_SHIFT) - 1,
+			memory_type_name_map[md->type].name);
+
+	free_bootmem(paddr, size);
+	*total += size;
+
+	return MEMMAP_WALK_CONT;
+}
+
+/*
+ * Called from setup_arch with interrupts disabled.
+ */
+void __init efi_enter_virtual_mode(void)
+{
+	void *newmap;
+	efi_status_t status;
+	u64 mapsize, total_freed = 0;
+	int count;
+
+	if (!efi_enabled(EFI_BOOT)) {
+		pr_info("EFI services will not be available.\n");
+		return;
+	}
+
+	pr_info("Remapping and enabling EFI services.\n");
+
+	mapsize = memmap.map_end - memmap.phys_map;
+	memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
+						   mapsize);
+	memmap.map_end = memmap.map + mapsize;
+
+	/* Map the regions we reserved earlier */
+	newmap = alloc_bootmem(mapsize);
+	if (newmap == NULL) {
+		pr_err("Failed to allocate new EFI memmap\n");
+		return;
+	}
+
+	count = remap_regions(newmap);
+	if (count <= 0) {
+		pr_err("Failed to remap EFI regions.\n");
+		return;
+	}
+
+	efi.memmap = &memmap;
+
+	efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
+	if (efi.systab)
+		set_bit(EFI_SYSTEM_TABLES, &arm_efi_facility);
+
+	/*
+	 * efi.systab->runtime is a pointer to something guaranteed by
+	 * the UEFI specification to be 1:1 mapped.
+	 */
+	runtime = (__force void *)efi_lookup_mapped_addr((u64)efi.systab->runtime);
+
+	/* Call SetVirtualAddressMap with the physical address of the map */
+	efi.set_virtual_address_map = runtime->set_virtual_address_map;
+
+	/* boot time idmap_pg_dir is incomplete, so fill in missing parts */
+	efi_setup_idmap();
+
+	cpu_switch_mm(idmap_pg_dir, &init_mm);
+	flush_tlb_all();
+	flush_cache_all();
+
+	status = efi.set_virtual_address_map(count * memmap.desc_size,
+					     memmap.desc_size,
+					     memmap.desc_version,
+					     newmap);
+	cpu_set_reserved_ttbr0();
+	flush_tlb_all();
+	flush_cache_all();
+
+	free_bootmem((unsigned long)newmap, mapsize);
+
+	if (status != EFI_SUCCESS) {
+		pr_err("Failed to set EFI virtual address map! [%lx]\n",
+			status);
+		return;
+	}
+
+	pr_info("EFI Virtual address map set\n");
+
+	/* Okay to free boot services memory now */
+	memmap_walk(&memmap, free_boot_region, &total_freed, 0);
+	if (total_freed)
+		pr_info("Freed 0x%llx bytes of EFI boot services memory",
+			total_freed);
+
+	/* Set up runtime services function pointers */
+	efi.get_time = runtime->get_time;
+	efi.set_time = runtime->set_time;
+	efi.get_wakeup_time = runtime->get_wakeup_time;
+	efi.set_wakeup_time = runtime->set_wakeup_time;
+	efi.get_variable = runtime->get_variable;
+	efi.get_next_variable = runtime->get_next_variable;
+	efi.set_variable = runtime->set_variable;
+	efi.query_variable_info = runtime->query_variable_info;
+	efi.update_capsule = runtime->update_capsule;
+	efi.query_capsule_caps = runtime->query_capsule_caps;
+	efi.get_next_high_mono_count = runtime->get_next_high_mono_count;
+	efi.reset_system = runtime->reset_system;
+
+	set_bit(EFI_RUNTIME_SERVICES, &arm_efi_facility);
+}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 790871a..395ab1d 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -41,6 +41,7 @@
 #include <linux/memblock.h>
 #include <linux/of_fdt.h>
 #include <linux/of_platform.h>
+#include <linux/efi.h>
 
 #include <asm/fixmap.h>
 #include <asm/cputype.h>
@@ -55,6 +56,7 @@
 #include <asm/traps.h>
 #include <asm/memblock.h>
 #include <asm/psci.h>
+#include <asm/efi.h>
 
 unsigned int processor_id;
 EXPORT_SYMBOL(processor_id);
@@ -222,9 +224,13 @@ void __init setup_arch(char **cmdline_p)
 
 	arm64_memblock_init();
 
+	efi_init();
 	paging_init();
 	request_standard_resources();
 
+	if (efi_enabled(EFI_BOOT))
+		efi_enter_virtual_mode();
+
 	unflatten_device_tree();
 
 	psci_init();
diff --git a/include/linux/efi.h b/include/linux/efi.h
index bc5687d..510a6d0 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -655,7 +655,7 @@ extern int __init efi_setup_pcdp_console(char *);
 #define EFI_64BIT		5	/* Is the firmware 64-bit? */
 
 #ifdef CONFIG_EFI
-# ifdef CONFIG_X86
+# if defined(CONFIG_X86) || defined(CONFIG_ARM64)
 extern int efi_enabled(int facility);
 # else
 static inline int efi_enabled(int facility)
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-11-29 22:05 ` [PATCH 1/3] arm64: add EFI stub Mark Salter
@ 2013-12-03 18:38   ` Will Deacon
  2013-12-03 19:31     ` Roy Franz
  2013-12-03 19:31     ` Mark Salter
  2013-12-05 14:18   ` Catalin Marinas
  2013-12-06 12:12   ` Grant Likely
  2 siblings, 2 replies; 24+ messages in thread
From: Will Deacon @ 2013-12-03 18:38 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Ard Biesheuvel, Catalin Marinas,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

Hi Mark, Roy,

On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> This patch adds PE/COFF header fields to the start of the Image
> so that it appears as an EFI application to EFI firmware. An EFI
> stub is included to allow direct booting of the kernel Image. Due
> to EFI firmware limitations, only little endian kernels with 4K
> page sizes are supported at this time. Support in the COFF header
> for signed images was provided by Ard Biesheuvel.

I haven't really jumped into this but, whilst I see the use of EFI_STUB on
both arm and arm64, there seems to be some duplication/reinvention between
the two series you've put together.

Maybe I'm just being ignorant, but the stuff in efi-stub.c really looks to
be doing the same thing on both architectures. Would you guys be able to
work to together to produce an independent series containing the common
parts, then add arm/arm64 backends on top of that please? In particular,
factoring out the device-tree parts ensures that we don't introduce subtle
differences between the two architectures when there's no real need to do
so...

...or shout at me because I didn't understand what you were doing!

Cheers,

Will

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-03 18:38   ` Will Deacon
@ 2013-12-03 19:31     ` Roy Franz
  2013-12-03 19:31     ` Mark Salter
  1 sibling, 0 replies; 24+ messages in thread
From: Roy Franz @ 2013-12-03 19:31 UTC (permalink / raw)
  To: Will Deacon
  Cc: Mark Salter, linux-kernel, patches, Ard Biesheuvel,
	Catalin Marinas, linux-arm-kernel, matt.fleming, linux-efi,
	Leif Lindholm

On Tue, Dec 3, 2013 at 10:38 AM, Will Deacon <will.deacon@arm.com> wrote:
> Hi Mark, Roy,
>
> On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
>> This patch adds PE/COFF header fields to the start of the Image
>> so that it appears as an EFI application to EFI firmware. An EFI
>> stub is included to allow direct booting of the kernel Image. Due
>> to EFI firmware limitations, only little endian kernels with 4K
>> page sizes are supported at this time. Support in the COFF header
>> for signed images was provided by Ard Biesheuvel.
>
> I haven't really jumped into this but, whilst I see the use of EFI_STUB on
> both arm and arm64, there seems to be some duplication/reinvention between
> the two series you've put together.
>
> Maybe I'm just being ignorant, but the stuff in efi-stub.c really looks to
> be doing the same thing on both architectures. Would you guys be able to
> work to together to produce an independent series containing the common
> parts, then add arm/arm64 backends on top of that please? In particular,
> factoring out the device-tree parts ensures that we don't introduce subtle
> differences between the two architectures when there's no real need to do
> so...
>
> ...or shout at me because I didn't understand what you were doing!
>
> Cheers,
>
> Will

Hi Will,

   Some of the device tree code is already factored out in
drivers/firmware/efi/fdt.c, but
looking at this again I think that there is more that can be moved to
common code.  The
main difference between the arm/arm64 stubs is the restrictions on
where the kernel binary
can be loaded.  On arm64 it is the kernel image itself, and on arm it
is the zImage that is the
next stage.  This also affects the build environment - the arm64 stub
is part of the kernel itself,
and the arm stub is part of the decompressor.
I'll work with Mark to see how much of the two stubs we can refactor
into shared code.

Roy

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-03 18:38   ` Will Deacon
  2013-12-03 19:31     ` Roy Franz
@ 2013-12-03 19:31     ` Mark Salter
  1 sibling, 0 replies; 24+ messages in thread
From: Mark Salter @ 2013-12-03 19:31 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-kernel, patches, Ard Biesheuvel, Catalin Marinas,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Tue, 2013-12-03 at 18:38 +0000, Will Deacon wrote:
> Hi Mark, Roy,
> 
> On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > This patch adds PE/COFF header fields to the start of the Image
> > so that it appears as an EFI application to EFI firmware. An EFI
> > stub is included to allow direct booting of the kernel Image. Due
> > to EFI firmware limitations, only little endian kernels with 4K
> > page sizes are supported at this time. Support in the COFF header
> > for signed images was provided by Ard Biesheuvel.
> 
> I haven't really jumped into this but, whilst I see the use of EFI_STUB on
> both arm and arm64, there seems to be some duplication/reinvention between
> the two series you've put together.

Indeed. As the file banner says, the arm64 stub started out from the arm
code.

> 
> Maybe I'm just being ignorant, but the stuff in efi-stub.c really looks to
> be doing the same thing on both architectures. Would you guys be able to
> work to together to produce an independent series containing the common
> parts, then add arm/arm64 backends on top of that please? In particular,
> factoring out the device-tree parts ensures that we don't introduce subtle
> differences between the two architectures when there's no real need to do
> so...
> 
> ...or shout at me because I didn't understand what you were doing!

Along the way, Roy has pulled out common bits into:
   drivers/firmware/efi-stub-helper.c
and
   drivers/firmware/fdt.c

There are arguably more bits which could be made common but there's not
really a lot left. There are differences between the two stubs which
limit what we can do. The arm stub is not part of the kernel (it is part
of the zImage wrapper) but the arm64 stub is. So arm64 has a little more
flexibility for using kernel facilities (__init attribute, LIBFDT, etc).

--Mark



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 2/3] doc: arm64: add description of EFI stub support
  2013-11-29 22:05 ` [PATCH 2/3] doc: arm64: add description of EFI stub support Mark Salter
@ 2013-12-05 12:53   ` Grant Likely
  0 siblings, 0 replies; 24+ messages in thread
From: Grant Likely @ 2013-12-05 12:53 UTC (permalink / raw)
  To: Mark Salter, linux-kernel
  Cc: matt.fleming, patches, Catalin Marinas, linux-doc, Will Deacon,
	Leif Lindholm, roy.franz, linux-efi, Rob Landley, Mark Salter,
	linux-arm-kernel

On Fri, 29 Nov 2013 17:05:11 -0500, Mark Salter <msalter@redhat.com> wrote:

Even documentation updates should have a mildly sane commit text. State
why these changes came about. Give a future reader at least some clues
as to why these changes were made now.

Otherwise:

Acked-by: Grant Likely <grant.likely@linaro.org>

g.

> Signed-off-by: Mark Salter <msalter@redhat.com>
> CC: Catalin Marinas <catalin.marinas@arm.com>
> CC: Will Deacon <will.deacon@arm.com>
> CC: linux-arm-kernel@lists.infradead.org
> CC: matt.fleming@intel.com
> CC: linux-efi@vger.kernel.org
> CC: linux-doc@vger.kernel.org
> CC: Rob Landley <rob@landley.net> 
> CC: Leif Lindholm <leif.lindholm@linaro.org>
> CC: roy.franz@linaro.org
> ---
>  Documentation/arm64/booting.txt |  4 ++++
>  Documentation/efi-stub.txt      | 10 ++++++++--
>  2 files changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/arm64/booting.txt b/Documentation/arm64/booting.txt
> index a9691cc..aa95d38c 100644
> --- a/Documentation/arm64/booting.txt
> +++ b/Documentation/arm64/booting.txt
> @@ -85,6 +85,10 @@ The decompressed kernel image contains a 64-byte header as follows:
>  Header notes:
>  
>  - code0/code1 are responsible for branching to stext.
> +- when booting through EFI, code0/code1 are initially skipped.
> +  res5 is an offset to the PE header and the PE header has the EFI
> +  entry point (efi_stub_entry). When the stub has done its work, it
> +  jumps to code0 to resume the normal boot process.
>  
>  The image must be placed at the specified offset (currently 0x80000)
>  from the start of the system RAM and called there. The start of the
> diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
> index 19e897c..c2a4b11 100644
> --- a/Documentation/efi-stub.txt
> +++ b/Documentation/efi-stub.txt
> @@ -12,6 +12,11 @@ arch/arm/boot/compressed/efi-header.S and
>  arch/arm/boot/compressed/efi-stub.c.  EFI stub code that is shared
>  between architectures is in drivers/firmware/efi/efi-stub-helper.c.
>  
> +For arm64, there is no compressed kernel support, so the Image itself
> +masquerades as a PE/COFF image and the EFI stub is linked into the
> +kernel. The arm64 EFI stub lives in arch/arm64/kernel/efi-entry.S
> +and arch/arm64/kernel/efi-stub.c.
> +
>  By using the EFI boot stub it's possible to boot a Linux kernel
>  without the use of a conventional EFI boot loader, such as grub or
>  elilo. Since the EFI boot stub performs the jobs of a boot loader, in
> @@ -28,7 +33,8 @@ the extension the EFI firmware loader will refuse to execute it. It's
>  not possible to execute bzImage.efi from the usual Linux file systems
>  because EFI firmware doesn't have support for them.  For ARM the
>  arch/arm/boot/zImage should be copied to the system partition, and it
> -may not need to be renamed.
> +may not need to be renamed. Similarly for arm64, arch/arm64/boot/Image
> +should be copied but not necessarily renamed.
>  
>  
>  **** Passing kernel parameters from the EFI shell
> @@ -72,7 +78,7 @@ is passed to bzImage.efi.
>  
>  **** The "dtb=" option
>  
> -For the ARM architecture, we also need to be able to provide a device
> +For ARM and arm64 architecture, we also need to be able to provide a device
>  tree to the kernel.  This is done with the "dtb=" command line option,
>  and is process in the same manner as the "initrd=" option that is described
>  above.
> -- 
> 1.8.3.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-11-29 22:05 ` [PATCH 1/3] arm64: add EFI stub Mark Salter
  2013-12-03 18:38   ` Will Deacon
@ 2013-12-05 14:18   ` Catalin Marinas
  2013-12-05 14:43     ` Mark Salter
  2013-12-06 14:55     ` Mark Salter
  2013-12-06 12:12   ` Grant Likely
  2 siblings, 2 replies; 24+ messages in thread
From: Catalin Marinas @ 2013-12-05 14:18 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

Hi Mark,

On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> This patch adds PE/COFF header fields to the start of the Image
> so that it appears as an EFI application to EFI firmware. An EFI
> stub is included to allow direct booting of the kernel Image. Due
> to EFI firmware limitations, only little endian kernels with 4K
> page sizes are supported at this time.

I don't fully understand the EFI firmware limitations but for big endian
we could have the EFI_STUB wrapper in little endian and get the kernel
to switch to big endian once booted. The image header should always be
little endian.

And I have to dig further into the 4K limitation (or you could give a
quick summary ;)).

> diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S
> new file mode 100644
> index 0000000..5f6d179
> --- /dev/null
> +++ b/arch/arm64/kernel/efi-entry.S
> @@ -0,0 +1,81 @@
> +/*
> + * EFI entry point.
> + *
> + * Copyright (C) 2013 Red Hat, Inc.
> + * Author: Mark Salter <msalter@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +
> +#include <asm/assembler.h>
> +
> +#define EFI_LOAD_ERROR 0x8000000000000001

It's defined already but I see why you can't include efi.h here. Maybe a
comment.

> +
> +       __INIT
> +
> +       /*
> +        * We arrive here from the EFI boot manager with:
> +        *
> +        *    * MMU on with identity-mapped RAM.
> +        *    * Icache and Dcache on
> +        *
> +        * We will most likely be running from some place other than where
> +        * we want to be. The kernel image wants to be placed at TEXT_OFFSET
> +        * from start of RAM.
> +        */
> +ENTRY(efi_stub_entry)
> +       stp     x29, x30, [sp, #-32]!
> +
> +       /*
> +        * Call efi_entry to do the real work.
> +        * x0 and x1 are already set up by firmware. Current runtime
> +        * address of image is calculated and passed via *image_addr.
> +        *
> +        * unsigned long efi_entry(void *handle,
> +        *                         efi_system_table_t *sys_table,
> +        *                         unsigned long *image_addr) ;
> +        */
> +       adrp    x8, _text
> +        add    x8, x8, #:lo12:_text

Minor: some wrong whitespace (but I don't trust our incoming mail server
either, it corrupts patches usually).

> +       add     x2, sp, 16
> +       str     x8, [x2]
> +       bl      efi_entry
> +       cmn     x0, #1
> +       b.eq    efi_load_fail
> +
> +       /*
> +        * efi_entry() will have relocated the kernel image if necessary
> +        * and we return here with device tree address in x0 and the kernel
> +        * entry point stored at *image_addr. Save those values in registers
> +        * which are preserved by __flush_dcache_all.
> +        */
> +       ldr     x1, [sp, #16]
> +       mov     x20, x0
> +       mov     x21, x1
> +
> +       bl      __flush_dcache_all

Regarding __flush_dcache_all, I plan to remove it for all cases apart
from power management with the MMU disabled. With MMU enabled, there is
no guarantee that this function does the right thing. It's even worse in
the guest context.

> +       /* Turn off Dcache and MMU */
> +       mrs     x0, sctlr_el1
> +       bic     x0, x0, #1 << 0 // clear SCTLR.M
> +       bic     x0, x0, #1 << 2 // clear SCTLR.C
> +       msr     sctlr_el1, x0
> +       isb

I assume an EFI app is running with the MMU enabled (and UP only). Do we
always run it in EL1? What about EL2 mode (needed by KVM and Xen)?

> +
> +       /* Jump to real entry point */
> +       mov     x0, x20
> +       mov     x1, xzr
> +       mov     x2, xzr
> +       mov     x3, xzr
> +       br      x21
> +
> +efi_load_fail:
> +       mov     x0, EFI_LOAD_ERROR

Needs #EFI_LOAD_ERROR (strange that gas doesn't complain).

> +       ldp     x29, x30, [sp], #32
> +       ret
> +
> +ENDPROC(efi_stub_entry)
> diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c
> new file mode 100644
> index 0000000..f000b04
> --- /dev/null
> +++ b/arch/arm64/kernel/efi-stub.c
> @@ -0,0 +1,280 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> + *
> + * This file implements the EFI boot stub for the arm64 kernel.
> + * Adapted from ARM version by Mark Salter <msalter@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/efi.h>
> +#include <linux/libfdt.h>
> +#include <asm/sections.h>
> +#include <generated/compile.h>
> +#include <linux/uts.h>
> +#include <linux/utsname.h>
> +#include <generated/utsrelease.h>
> +#include <linux/version.h>
> +
> +/* error code which can't be mistaken for valid address */
> +#define EFI_ERROR      (~0UL)
> +
> +/*
> + * EFI function call wrappers. These are not required for arm64, but wrappers
> + * are required for X86 to convert between ABIs. These wrappers are provided
> + * to allow code sharing between X86 and other architectures. Since these
> + * wrappers directly invoke the EFI function pointer, the function pointer
> + * type must be properly defined, which is not the case for X86. One advantage
> + * of this is it allows for type checking of arguments, which is not possible
> + * with the X86 wrappers.
> + */
> +#define efi_call_phys0(f)                      f()
> +#define efi_call_phys1(f, a1)                  f(a1)
> +#define efi_call_phys2(f, a1, a2)              f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3)          f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4)      f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)  f(a1, a2, a3, a4, a5)
> +
> +/*
> + * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
> + * start of kernel and may not cross a 2MiB boundary. We set alignment to
> + * equal max size so we know it won't cross a 2MiB boudary.
> + */
> +#define MAX_DTB_SIZE   0x40000

2MB is 0x200000 (or I don't understand the comment).

> +#define DTB_ALIGN      MAX_DTB_SIZE
> +#define MAX_DTB_OFFSET 0x20000000
> +
> +#define pr_efi(msg)     efi_printk(sys_table, "EFI stub: "msg)
> +#define pr_efi_err(msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
> +
> +struct fdt_region {
> +       u64 base;
> +       u64 size;
> +};
> +
> +/* Include shared EFI stub code */
> +#include "../../../drivers/firmware/efi/efi-stub-helper.c"
> +#include "../../../drivers/firmware/efi/fdt.c"

I don't particularly like .c files inclusion but it looks like x86 does
the same.

> +
> +static unsigned long __init get_dram_base(efi_system_table_t *sys_table)
> +{
> +       efi_status_t status;
> +       unsigned long map_size, desc_size;
> +       unsigned long membase = EFI_ERROR;
> +       efi_memory_desc_t *memory_map;
> +       int i;
> +
> +       status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +                                   &desc_size, NULL, NULL);
> +       if (status == EFI_SUCCESS) {

Can you exit earlier here if !EFI_SUCCESS? It reduces the indentation
level.

-- 
Catalin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-05 14:18   ` Catalin Marinas
@ 2013-12-05 14:43     ` Mark Salter
  2013-12-05 15:28       ` Catalin Marinas
  2013-12-06 14:55     ` Mark Salter
  1 sibling, 1 reply; 24+ messages in thread
From: Mark Salter @ 2013-12-05 14:43 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > This patch adds PE/COFF header fields to the start of the Image
> > so that it appears as an EFI application to EFI firmware. An EFI
> > stub is included to allow direct booting of the kernel Image. Due
> > to EFI firmware limitations, only little endian kernels with 4K
> > page sizes are supported at this time.
> 
> I don't fully understand the EFI firmware limitations but for big endian
> we could have the EFI_STUB wrapper in little endian and get the kernel
> to switch to big endian once booted. The image header should always be
> little endian.

That would be fun. :) You'd also have to switch back and forth to make
EFI runtime services calls.

> 
> And I have to dig further into the 4K limitation (or you could give a
> quick summary ;)).

Just that the current UEFI spec mandates 4K pages for UEFI. So if UEFI
maps two 4k pages with different attributes and those two pages are
within the same 64k kernel page, there'd be a problem. I'd be better if
UEFI used 64k pages, then the kernel could easily use 4k or 64k.

--Mark



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-11-29 22:05 ` [PATCH 3/3] arm64: add EFI runtime services Mark Salter
@ 2013-12-05 15:25   ` Catalin Marinas
  2013-12-05 15:52     ` Mark Salter
  2013-12-06 14:34     ` Mark Salter
  2013-12-09 13:52   ` Leif Lindholm
  1 sibling, 2 replies; 24+ messages in thread
From: Catalin Marinas @ 2013-12-05 15:25 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Will Deacon, linux-arm-kernel,
	matt.fleming, linux-efi, Leif Lindholm, roy.franz

On Fri, Nov 29, 2013 at 10:05:12PM +0000, Mark Salter wrote:
> diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
> new file mode 100644
> index 0000000..7384048
> --- /dev/null
> +++ b/arch/arm64/include/asm/efi.h
> @@ -0,0 +1,18 @@
> +#ifndef _ASM_ARM64_EFI_H
> +#define _ASM_ARM64_EFI_H
> +
> +#include <asm/io.h>
> +
> +#ifdef CONFIG_EFI
> +extern void efi_init(void);
> +#else
> +#define efi_init()
> +#endif
> +
> +#define efi_remap(cookie, size) __ioremap((cookie), (size), PAGE_KERNEL_EXEC)
> +#define efi_ioremap(cookie, size) __ioremap((cookie), (size), \
> +                                           __pgprot(PROT_DEVICE_nGnRE))
> +#define efi_unmap(cookie) __iounmap((cookie))
> +#define efi_iounmap(cookie) __iounmap((cookie))

I prefer to use the ioremap_*() functions rather than the lower-level
__ioremap(). The ioremap_cache() should give us executable memory.

Looking at the code, I think we have a bug with ioremap_cache() using
MT_NORMAL since it doesn't have the shareability bit (Device memory does
not require this on AArch64). PROT_DEFAULT should change to
pgprot_default | PTE_DIRTY.

> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> new file mode 100644
> index 0000000..1bad8a7
> --- /dev/null
> +++ b/arch/arm64/kernel/efi.c
> @@ -0,0 +1,507 @@
> +/*
> + * Extensible Firmware Interface
> + *
> + * Based on Extensible Firmware Interface Specification version 2.3.1
> + *
> + * Copyright (C) 2013 Linaro Ltd.
> + *
> + * Adapted for arm64 from arch/arm/kernel/efi.c code
> + */
> +
> +#include <linux/efi.h>
> +#include <linux/export.h>
> +#include <linux/memblock.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/bootmem.h>
> +
> +#include <asm/cacheflush.h>
> +#include <asm/efi.h>
> +#include <asm/tlbflush.h>
> +#include <asm/mmu_context.h>
> +
> +#define efi_early_remap(a, b) \
> +       ((__force void *)early_ioremap((a), (b)))
> +#define efi_early_unmap(a, b) \
> +       early_iounmap((void __iomem *)(a), (b))

I lost track of the early_ioremap status for arm/arm64? Was there more
progress since October (I think)?

> +static int __init fdt_find_efi_params(unsigned long node, const char *uname,
> +                                     int depth, void *data)
> +{
> +       unsigned long len, size;
> +       __be32 *prop;
> +
> +       if (depth != 1 ||
> +           (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
> +               return 0;
> +
> +       pr_info("Getting EFI parameters from FDT.\n");
> +
> +       prop = of_get_flat_dt_prop(node, "linux,uefi-system-table", &len);
> +       if (!prop) {
> +               pr_err("No EFI system table in FDT\n");
> +               return 0;
> +       }
> +       efi_system_table = of_read_ulong(prop, len/4);
> +
> +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-start", &len);
> +       if (!prop) {
> +               pr_err("No EFI memmap in FDT\n");
> +               return 0;
> +       }
> +       memmap.phys_map = (void *)of_read_ulong(prop, len/4);
> +
> +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-size", &len);
> +       if (!prop) {
> +               pr_err("No EFI memmap size in FDT\n");
> +               return 0;
> +       }
> +       size = of_read_ulong(prop, len/4);
> +       memmap.map_end = memmap.phys_map + size;
> +
> +       /* reserve memmap */
> +       memblock_reserve((u64)memmap.phys_map, size);
> +
> +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-size", &len);
> +       if (!prop) {
> +               pr_err("No EFI memmap descriptor size in FDT\n");
> +               return 0;
> +       }
> +       memmap.desc_size = of_read_ulong(prop, len/4);
> +
> +       memmap.nr_map = size / memmap.desc_size;
> +
> +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-ver", &len);
> +       if (!prop) {
> +               pr_err("No EFI memmap descriptor version in FDT\n");
> +               return 0;
> +       }
> +       memmap.desc_version = of_read_ulong(prop, len/4);
> +
> +       if (uefi_debug) {
> +               pr_info("  EFI system table @ %p\n", (void *)efi_system_table);
> +               pr_info("  EFI mmap @ %p-%p\n", memmap.phys_map,
> +                       memmap.map_end);
> +               pr_info("  EFI mmap descriptor size = 0x%lx\n",
> +                       memmap.desc_size);
> +               pr_info("  EFI mmap descriptor version = 0x%lx\n",
> +                       memmap.desc_version);
> +       }
> +
> +       return 1;
> +}

Are these checks generic to other architectures? We may do with some
helpers to avoid duplication.

> +
> +#define PGD_END  (&idmap_pg_dir[sizeof(idmap_pg_dir)/sizeof(pgd_t)])

Just &idmap_pg_dir[PTRS_PER_PGD] would do (or idmap_pg_dir +
ARRAY_SIZE(idmap_pg_dir)).

> +#ifndef CONFIG_SMP
> +#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF)
> +#else
> +#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF | \
> +                   PMD_SECT_S)
> +#endif
> +
> +static void __init create_idmap(unsigned long addr, unsigned long len)

I would change this to use the existing create_mapping() function which
takes care of allocating pud/pmd/ptes. We shouldn't duplicate this
functionality in two places. create_mapping() may need a slight change
since it assumes swapper_pg_dir but it's not much. It also uses
memblock_alloc() for early allocations.

> +static __init int memmap_walk(struct efi_memory_map *memmap,
> +                             int (*func)(efi_memory_desc_t *, void *),
> +                             void *private_data, int early)

Is this generic enough as a common helper between arm and arm64 (and
maybe x86)?

> +static __init int reserve_region(efi_memory_desc_t *md, void *priv)
[...]
> +static __init void reserve_regions(void)
[...]
> +static int __init remap_region(efi_memory_desc_t *md, void *priv)
[...]
> +static int __init remap_regions(efi_memory_desc_t *map)

Same here (I haven't looked at the other implementations).

> +/*
> + * Called from setup_arch with interrupts disabled.
> + */
> +void __init efi_enter_virtual_mode(void)
> +{
> +       void *newmap;
> +       efi_status_t status;
> +       u64 mapsize, total_freed = 0;
> +       int count;
> +
> +       if (!efi_enabled(EFI_BOOT)) {
> +               pr_info("EFI services will not be available.\n");
> +               return;
> +       }
> +
> +       pr_info("Remapping and enabling EFI services.\n");
> +
> +       mapsize = memmap.map_end - memmap.phys_map;
> +       memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
> +                                                  mapsize);
> +       memmap.map_end = memmap.map + mapsize;
> +
> +       /* Map the regions we reserved earlier */
> +       newmap = alloc_bootmem(mapsize);

memblock_alloc() (also check the other bootmem uses in this patch, arm64
is using memblock).

> +       if (newmap == NULL) {
> +               pr_err("Failed to allocate new EFI memmap\n");
> +               return;
> +       }
> +
> +       count = remap_regions(newmap);
> +       if (count <= 0) {
> +               pr_err("Failed to remap EFI regions.\n");
> +               return;
> +       }
> +
> +       efi.memmap = &memmap;
> +
> +       efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
> +       if (efi.systab)
> +               set_bit(EFI_SYSTEM_TABLES, &arm_efi_facility);
> +
> +       /*
> +        * efi.systab->runtime is a pointer to something guaranteed by
> +        * the UEFI specification to be 1:1 mapped.
> +        */
> +       runtime = (__force void *)efi_lookup_mapped_addr((u64)efi.systab->runtime);
> +
> +       /* Call SetVirtualAddressMap with the physical address of the map */
> +       efi.set_virtual_address_map = runtime->set_virtual_address_map;
> +
> +       /* boot time idmap_pg_dir is incomplete, so fill in missing parts */
> +       efi_setup_idmap();
> +
> +       cpu_switch_mm(idmap_pg_dir, &init_mm);
> +       flush_tlb_all();
> +       flush_cache_all();
> +
> +       status = efi.set_virtual_address_map(count * memmap.desc_size,
> +                                            memmap.desc_size,
> +                                            memmap.desc_version,
> +                                            newmap);
> +       cpu_set_reserved_ttbr0();
> +       flush_tlb_all();
> +       flush_cache_all();

What is the point of cache flusing here? See my comment in the first
patch about this not being guaranteed.

> +
> +       free_bootmem((unsigned long)newmap, mapsize);

memblock.

-- 
Catalin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-05 14:43     ` Mark Salter
@ 2013-12-05 15:28       ` Catalin Marinas
  2013-12-06 12:25         ` Grant Likely
  0 siblings, 1 reply; 24+ messages in thread
From: Catalin Marinas @ 2013-12-05 15:28 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Thu, Dec 05, 2013 at 02:43:23PM +0000, Mark Salter wrote:
> On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> > On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > > This patch adds PE/COFF header fields to the start of the Image
> > > so that it appears as an EFI application to EFI firmware. An EFI
> > > stub is included to allow direct booting of the kernel Image. Due
> > > to EFI firmware limitations, only little endian kernels with 4K
> > > page sizes are supported at this time.
> > 
> > I don't fully understand the EFI firmware limitations but for big endian
> > we could have the EFI_STUB wrapper in little endian and get the kernel
> > to switch to big endian once booted. The image header should always be
> > little endian.
> 
> That would be fun. :) You'd also have to switch back and forth to make
> EFI runtime services calls.

OK, we'll have to live with this restriction.

> > And I have to dig further into the 4K limitation (or you could give a
> > quick summary ;)).
> 
> Just that the current UEFI spec mandates 4K pages for UEFI. So if UEFI
> maps two 4k pages with different attributes and those two pages are
> within the same 64k kernel page, there'd be a problem. I'd be better if
> UEFI used 64k pages, then the kernel could easily use 4k or 64k.

For server space, we may see some people asking for 64K pages. But I
don't know whether there are any UEFI plans here.

Thanks.

-- 
Catalin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-12-05 15:25   ` Catalin Marinas
@ 2013-12-05 15:52     ` Mark Salter
  2013-12-05 15:59       ` Catalin Marinas
  2013-12-06 14:34     ` Mark Salter
  1 sibling, 1 reply; 24+ messages in thread
From: Mark Salter @ 2013-12-05 15:52 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: linux-kernel, patches, Will Deacon, linux-arm-kernel,
	matt.fleming, linux-efi, Leif Lindholm, roy.franz

On Thu, 2013-12-05 at 15:25 +0000, Catalin Marinas wrote:
> I lost track of the early_ioremap status for arm/arm64? Was there more
> progress since October (I think)?

See the two patch series:

 https://lkml.org/lkml/2013/11/25/474

and

 https://lkml.org/lkml/2013/11/27/621

The latter early_ioremap depends on the first although there are no
arm64 bits in the first. All of the arm64 early_ioremap stuff is in
the latter.

--Mark



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-12-05 15:52     ` Mark Salter
@ 2013-12-05 15:59       ` Catalin Marinas
  0 siblings, 0 replies; 24+ messages in thread
From: Catalin Marinas @ 2013-12-05 15:59 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Will Deacon, linux-arm-kernel,
	matt.fleming, linux-efi, Leif Lindholm, roy.franz

On Thu, Dec 05, 2013 at 03:52:41PM +0000, Mark Salter wrote:
> On Thu, 2013-12-05 at 15:25 +0000, Catalin Marinas wrote:
> > I lost track of the early_ioremap status for arm/arm64? Was there more
> > progress since October (I think)?
> 
> See the two patch series:
> 
>  https://lkml.org/lkml/2013/11/25/474
> 
> and
> 
>  https://lkml.org/lkml/2013/11/27/621
> 
> The latter early_ioremap depends on the first although there are no
> arm64 bits in the first. All of the arm64 early_ioremap stuff is in
> the latter.

Thanks. I was even cc'ed but too many emails in my inbox ;). I'll have a
look.

-- 
Catalin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-11-29 22:05 ` [PATCH 1/3] arm64: add EFI stub Mark Salter
  2013-12-03 18:38   ` Will Deacon
  2013-12-05 14:18   ` Catalin Marinas
@ 2013-12-06 12:12   ` Grant Likely
  2 siblings, 0 replies; 24+ messages in thread
From: Grant Likely @ 2013-12-06 12:12 UTC (permalink / raw)
  To: Mark Salter, linux-kernel
  Cc: patches, Mark Salter, Ard Biesheuvel, Catalin Marinas,
	Will Deacon, linux-arm-kernel, matt.fleming, linux-efi,
	Leif Lindholm, roy.franz

On Fri, 29 Nov 2013 17:05:10 -0500, Mark Salter <msalter@redhat.com> wrote:
> This patch adds PE/COFF header fields to the start of the Image
> so that it appears as an EFI application to EFI firmware. An EFI
> stub is included to allow direct booting of the kernel Image. Due
> to EFI firmware limitations, only little endian kernels with 4K
> page sizes are supported at this time. Support in the COFF header
> for signed images was provided by Ard Biesheuvel.
> 
> Signed-off-by: Mark Salter <msalter@redhat.com>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

Reviewed-by: Grant Likely <grant.likely@linaro.org>

I've already made comments on Roy's arm32 version of this code. I don't
like the duplication and it needs to be consolidated, but I would be
fine with consolidation being done as follow-on patches if that
expedites getting the code in.

g.

> CC: Catalin Marinas <catalin.marinas@arm.com>
> CC: Will Deacon <will.deacon@arm.com>
> CC: linux-arm-kernel@lists.infradead.org
> CC: matt.fleming@intel.com
> CC: linux-efi@vger.kernel.org
> CC: Leif Lindholm <leif.lindholm@linaro.org>
> CC: roy.franz@linaro.org
> ---
>  arch/arm64/Kconfig            |  10 ++
>  arch/arm64/kernel/Makefile    |   3 +
>  arch/arm64/kernel/efi-entry.S |  81 ++++++++++++
>  arch/arm64/kernel/efi-stub.c  | 280 ++++++++++++++++++++++++++++++++++++++++++
>  arch/arm64/kernel/head.S      | 112 +++++++++++++++++
>  5 files changed, 486 insertions(+)
>  create mode 100644 arch/arm64/kernel/efi-entry.S
>  create mode 100644 arch/arm64/kernel/efi-stub.c
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 809c1b8..10b0e93 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -250,6 +250,16 @@ config CMDLINE_FORCE
>  	  This is useful if you cannot or don't want to change the
>  	  command-line options your boot loader passes to the kernel.
>  
> +config EFI_STUB
> +	bool "EFI stub support"
> +	depends on !CPU_BIG_ENDIAN && !ARM64_64K_PAGES && OF
> +	select LIBFDT
> +	default y
> +	help
> +	  This kernel feature allows an Image to be loaded directly
> +	  by EFI firmware without the use of a bootloader.
> +	  See Documentation/efi-stub.txt for more information.
> +
>  endmenu
>  
>  menu "Userspace binary formats"
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index 5ba2fd4..1c52b84 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -4,6 +4,8 @@
>  
>  CPPFLAGS_vmlinux.lds	:= -DTEXT_OFFSET=$(TEXT_OFFSET)
>  AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)
> +CFLAGS_efi-stub.o 	:= -DTEXT_OFFSET=$(TEXT_OFFSET) \
> +			   -I$(src)/../../../scripts/dtc/libfdt
>  
>  # Object file lists.
>  arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\
> @@ -18,6 +20,7 @@ arm64-obj-$(CONFIG_SMP)			+= smp.o smp_spin_table.o
>  arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
>  arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
>  arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
> +arm64-obj-$(CONFIG_EFI_STUB)		+= efi-stub.o efi-entry.o
>  
>  obj-y					+= $(arm64-obj-y) vdso/
>  obj-m					+= $(arm64-obj-m)
> diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S
> new file mode 100644
> index 0000000..5f6d179
> --- /dev/null
> +++ b/arch/arm64/kernel/efi-entry.S
> @@ -0,0 +1,81 @@
> +/*
> + * EFI entry point.
> + *
> + * Copyright (C) 2013 Red Hat, Inc.
> + * Author: Mark Salter <msalter@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/linkage.h>
> +#include <linux/init.h>
> +
> +#include <asm/assembler.h>
> +
> +#define EFI_LOAD_ERROR 0x8000000000000001
> +
> +	__INIT
> +
> +	/*
> +	 * We arrive here from the EFI boot manager with:
> +	 *
> +	 *    * MMU on with identity-mapped RAM.
> +	 *    * Icache and Dcache on
> +	 *
> +	 * We will most likely be running from some place other than where
> +	 * we want to be. The kernel image wants to be placed at TEXT_OFFSET
> +	 * from start of RAM.
> +	 */
> +ENTRY(efi_stub_entry)
> +	stp	x29, x30, [sp, #-32]!
> +
> +	/*
> +	 * Call efi_entry to do the real work.
> +	 * x0 and x1 are already set up by firmware. Current runtime
> +	 * address of image is calculated and passed via *image_addr.
> +	 *
> +	 * unsigned long efi_entry(void *handle,
> +	 *                         efi_system_table_t *sys_table,
> +	 *                         unsigned long *image_addr) ;
> +	 */
> +	adrp	x8, _text
> +        add	x8, x8, #:lo12:_text
> +	add	x2, sp, 16
> +	str	x8, [x2]
> +	bl	efi_entry
> +	cmn	x0, #1
> +	b.eq	efi_load_fail
> +
> +	/*
> +	 * efi_entry() will have relocated the kernel image if necessary
> +	 * and we return here with device tree address in x0 and the kernel
> +	 * entry point stored at *image_addr. Save those values in registers
> +	 * which are preserved by __flush_dcache_all.
> +	 */
> +	ldr	x1, [sp, #16]
> +	mov	x20, x0
> +	mov	x21, x1
> +
> +	bl	__flush_dcache_all
> +	/* Turn off Dcache and MMU */
> +	mrs	x0, sctlr_el1
> +	bic	x0, x0, #1 << 0	// clear SCTLR.M
> +	bic	x0, x0, #1 << 2	// clear SCTLR.C
> +	msr	sctlr_el1, x0
> +	isb
> +
> +	/* Jump to real entry point */
> +	mov	x0, x20
> +	mov	x1, xzr
> +	mov	x2, xzr
> +	mov	x3, xzr
> +	br	x21
> +
> +efi_load_fail:
> +	mov	x0, EFI_LOAD_ERROR
> +	ldp	x29, x30, [sp], #32
> +	ret
> +
> +ENDPROC(efi_stub_entry)
> diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c
> new file mode 100644
> index 0000000..f000b04
> --- /dev/null
> +++ b/arch/arm64/kernel/efi-stub.c
> @@ -0,0 +1,280 @@
> +/*
> + * linux/arch/arm/boot/compressed/efi-stub.c
> + *
> + * Copyright (C) 2013 Linaro Ltd;  <roy.franz@linaro.org>
> + *
> + * This file implements the EFI boot stub for the arm64 kernel.
> + * Adapted from ARM version by Mark Salter <msalter@redhat.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/efi.h>
> +#include <linux/libfdt.h>
> +#include <asm/sections.h>
> +#include <generated/compile.h>
> +#include <linux/uts.h>
> +#include <linux/utsname.h>
> +#include <generated/utsrelease.h>
> +#include <linux/version.h>
> +
> +/* error code which can't be mistaken for valid address */
> +#define EFI_ERROR	(~0UL)
> +
> +/*
> + * EFI function call wrappers. These are not required for arm64, but wrappers
> + * are required for X86 to convert between ABIs. These wrappers are provided
> + * to allow code sharing between X86 and other architectures. Since these
> + * wrappers directly invoke the EFI function pointer, the function pointer
> + * type must be properly defined, which is not the case for X86. One advantage
> + * of this is it allows for type checking of arguments, which is not possible
> + * with the X86 wrappers.
> + */
> +#define efi_call_phys0(f)			f()
> +#define efi_call_phys1(f, a1)			f(a1)
> +#define efi_call_phys2(f, a1, a2)		f(a1, a2)
> +#define efi_call_phys3(f, a1, a2, a3)		f(a1, a2, a3)
> +#define efi_call_phys4(f, a1, a2, a3, a4)	f(a1, a2, a3, a4)
> +#define efi_call_phys5(f, a1, a2, a3, a4, a5)	f(a1, a2, a3, a4, a5)
> +
> +/*
> + * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
> + * start of kernel and may not cross a 2MiB boundary. We set alignment to
> + * equal max size so we know it won't cross a 2MiB boudary.
> + */
> +#define MAX_DTB_SIZE	0x40000
> +#define DTB_ALIGN	MAX_DTB_SIZE
> +#define MAX_DTB_OFFSET	0x20000000
> +
> +#define pr_efi(msg)     efi_printk(sys_table, "EFI stub: "msg)
> +#define pr_efi_err(msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
> +
> +struct fdt_region {
> +	u64 base;
> +	u64 size;
> +};
> +
> +/* Include shared EFI stub code */
> +#include "../../../drivers/firmware/efi/efi-stub-helper.c"
> +#include "../../../drivers/firmware/efi/fdt.c"
> +
> +static unsigned long __init get_dram_base(efi_system_table_t *sys_table)
> +{
> +	efi_status_t status;
> +	unsigned long map_size, desc_size;
> +	unsigned long membase = EFI_ERROR;
> +	efi_memory_desc_t *memory_map;
> +	int i;
> +
> +	status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +				    &desc_size, NULL, NULL);
> +	if (status == EFI_SUCCESS) {
> +		for (i = 0; i < (map_size / sizeof(efi_memory_desc_t)); i++) {
> +			efi_memory_desc_t *desc;
> +			unsigned long m = (unsigned long)memory_map;
> +
> +			desc = (efi_memory_desc_t *)(m + (i * desc_size));
> +
> +			if (desc->num_pages == 0)
> +				break;
> +
> +			if (desc->type == EFI_CONVENTIONAL_MEMORY) {
> +				unsigned long base = desc->phys_addr;
> +
> +				base &= ~((unsigned long)(TEXT_OFFSET - 1));
> +
> +				if (membase > base)
> +					membase = base;
> +			}
> +		}
> +	}
> +	return membase;
> +}
> +
> +unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table,
> +			       unsigned long *image_addr)
> +{
> +	efi_loaded_image_t *image;
> +	efi_status_t status;
> +	unsigned long image_size, image_memsize = 0;
> +	unsigned long dram_base;
> +	/* addr/point and size pairs for memory management*/
> +	u64 initrd_addr;
> +	u64 initrd_size = 0;
> +	u64 fdt_addr;  /* Original DTB */
> +	u64 fdt_size = 0;
> +	unsigned long new_fdt_size;
> +	char *cmdline_ptr;
> +	int cmdline_size = 0;
> +	unsigned long new_fdt_addr;
> +	unsigned long map_size, desc_size;
> +	unsigned long mmap_key;
> +	efi_memory_desc_t *memory_map;
> +	u32 desc_ver;
> +	efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID;
> +
> +	/* Check if we were booted by the EFI firmware */
> +	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
> +		goto fail;
> +
> +	pr_efi("Booting Linux Kernel...\n");
> +
> +	/* get the command line from EFI, using the LOADED_IMAGE protocol */
> +	status = efi_call_phys3(sys_table->boottime->handle_protocol,
> +				handle, &proto, (void *)&image);
> +	if (status != EFI_SUCCESS) {
> +		pr_efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
> +		goto fail;
> +	}
> +
> +	/*
> +	 * We are going to copy this into device tree, so we don't care where
> +	 * in memory it is.
> +	 */
> +	cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
> +						   &cmdline_size);
> +	if (!cmdline_ptr) {
> +		pr_efi_err("Failed to convert command line to ascii\n");
> +		goto fail;
> +	}
> +
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "dtb=",
> +				      ~0UL, (unsigned long *)&fdt_addr,
> +				      (unsigned long *)&fdt_size);
> +	if (status != EFI_SUCCESS) {
> +		pr_efi_err("Failed to load device tree blob\n");
> +		goto fail_free_cmdline;
> +	}
> +
> +	if (fdt_check_header((void *)fdt_addr)) {
> +		pr_efi_err("Device Tree header not valid\n");
> +		goto fail_free_dtb;
> +	}
> +	if (fdt_totalsize((void *)fdt_addr) > fdt_size) {
> +		pr_efi_err("Incomplete device tree\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	dram_base = get_dram_base(sys_table);
> +	if (dram_base == EFI_ERROR) {
> +		pr_efi_err("Failed to get DRAM base\n");
> +		goto fail_free_dtb;
> +	}
> +
> +	/* Relocate the image, if required. */
> +	image_size = image->image_size;
> +	if (*image_addr != (dram_base + TEXT_OFFSET)) {
> +		image_memsize = image_size + (_end - _edata);
> +		status = efi_relocate_kernel(sys_table, image_addr,
> +					     image_size, image_memsize,
> +					     dram_base + TEXT_OFFSET,
> +					     PAGE_SIZE);
> +		if (status != EFI_SUCCESS) {
> +			pr_efi_err("Failed to relocate kernel\n");
> +			goto fail_free_dtb;
> +		}
> +		if (*image_addr != (dram_base + TEXT_OFFSET)) {
> +			pr_efi_err("Failed to alloc kernel memory\n");
> +			goto fail_free_image;
> +		}
> +	}
> +
> +	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
> +				      dram_base + 0x20000000,
> +				      (unsigned long *)&initrd_addr,
> +				      (unsigned long *)&initrd_size);
> +	if (status != EFI_SUCCESS)
> +		pr_efi("No initrd found\n");
> +
> +	/*
> +	 * Estimate size of new FDT, and allocate memory for it. We
> +	 * will allocate a bigger buffer if this ends up being too
> +	 * small, so a rough guess is OK here. We increment the size
> +	 * by PAGE_SIZE since the firmware allocates by pages anyway.
> +	 */
> +	new_fdt_size = fdt_size + EFI_PAGE_SIZE;
> +	while (1) {
> +		status = efi_high_alloc(sys_table, new_fdt_size, DTB_ALIGN,
> +					&new_fdt_addr,
> +					dram_base + MAX_DTB_OFFSET);
> +		if (status != EFI_SUCCESS) {
> +			pr_efi_err("No memory for new device tree\n");
> +			goto fail_free_initrd;
> +		}
> +
> +		/*
> +		 * Now that we have done our final memory allocation, we can
> +		 * get the memory map key needed for exit_boot_services().
> +		 */
> +		status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> +					    &desc_size, &desc_ver, &mmap_key);
> +		if (status != EFI_SUCCESS)
> +			goto fail_free_new_fdt;
> +
> +		status = update_fdt(sys_table,
> +				    (void *)fdt_addr, (void *)new_fdt_addr,
> +				    new_fdt_size, cmdline_ptr,
> +				    initrd_addr, initrd_size,
> +				    memory_map, map_size, desc_size, desc_ver);
> +
> +		/* Succeeding the first time is the expected case. */
> +		if (status == EFI_SUCCESS)
> +			break;
> +
> +		if (status == EFI_BUFFER_TOO_SMALL) {
> +			/*
> +			 * We need to allocate more space for the new
> +			 * device tree, so free existing buffer that is
> +			 * too small.  Also free memory map, as we will need
> +			 * to get new one that reflects the free/alloc we do
> +			 * on the device tree buffer.
> +			 */
> +			efi_free(sys_table, new_fdt_size, new_fdt_addr);
> +			efi_call_phys1(sys_table->boottime->free_pool,
> +				       memory_map);
> +			new_fdt_size += EFI_PAGE_SIZE;
> +		} else {
> +			pr_efi_err("Unable to constuct new device tree\n");
> +			goto fail_free_mmap;
> +		}
> +	}
> +
> +	/* Now we are ready to exit_boot_services.*/
> +	status = efi_call_phys2(sys_table->boottime->exit_boot_services,
> +				handle, mmap_key);
> +
> +	if (status != EFI_SUCCESS) {
> +		pr_efi_err("Exit boot services failed\n");
> +		goto fail_free_mmap;
> +	}
> +
> +	/*
> +	 * Now we need to return the FDT address to the calling
> +	 * function so it can be used as part of normal boot.
> +	 */
> +	return new_fdt_addr;
> +
> +fail_free_mmap:
> +	efi_call_phys1(sys_table->boottime->free_pool, memory_map);
> +
> +fail_free_new_fdt:
> +	efi_free(sys_table, new_fdt_size, new_fdt_addr);
> +
> +fail_free_initrd:
> +	efi_free(sys_table, initrd_size, initrd_addr);
> +
> +fail_free_image:
> +	efi_free(sys_table, image_memsize, *image_addr);
> +
> +fail_free_dtb:
> +	if (fdt_addr)
> +		efi_free(sys_table, fdt_size, fdt_addr);
> +
> +fail_free_cmdline:
> +	efi_free(sys_table, cmdline_size, (u64)cmdline_ptr);
> +
> +fail:
> +	return EFI_ERROR;
> +}
> diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
> index 03adf8f..720429e 100644
> --- a/arch/arm64/kernel/head.S
> +++ b/arch/arm64/kernel/head.S
> @@ -107,8 +107,18 @@
>  	/*
>  	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
>  	 */
> +#ifdef CONFIG_EFI_STUB
> +	/*
> +	 * Magic "MZ" signature for PE/COFF
> +	 * Little Endian:  add x13, x18, #0x16
> +	 */
> +efi_head:
> +	.long   0x91005a4d
> +	b	stext
> +#else
>  	b	stext				// branch to kernel start, magic
>  	.long	0				// reserved
> +#endif
>  	.quad	TEXT_OFFSET			// Image load offset from start of RAM
>  	.quad	0				// reserved
>  	.quad	0				// reserved
> @@ -119,7 +129,109 @@
>  	.byte	0x52
>  	.byte	0x4d
>  	.byte	0x64
> +#ifdef CONFIG_EFI_STUB
> +	.long	pe_header - efi_head		// Offset to the PE header.
> +#else
>  	.word	0				// reserved
> +#endif
> +
> +#ifdef CONFIG_EFI_STUB
> +	.align 3
> +pe_header:
> +	.ascii	"PE"
> +	.short 	0
> +coff_header:
> +	.short	0xaa64				// AArch64
> +	.short	2				// nr_sections
> +	.long	0 				// TimeDateStamp
> +	.long	0				// PointerToSymbolTable
> +	.long	1				// NumberOfSymbols
> +	.short	section_table - optional_header	// SizeOfOptionalHeader
> +	.short	0x206				// Characteristics.
> +						// IMAGE_FILE_DEBUG_STRIPPED |
> +						// IMAGE_FILE_EXECUTABLE_IMAGE |
> +						// IMAGE_FILE_LINE_NUMS_STRIPPED
> +optional_header:
> +	.short	0x20b				// PE32+ format
> +	.byte	0x02				// MajorLinkerVersion
> +	.byte	0x14				// MinorLinkerVersion
> +	.long	_edata - stext			// SizeOfCode
> +	.long	0				// SizeOfInitializedData
> +	.long	0				// SizeOfUninitializedData
> +	.long	efi_stub_entry - efi_head	// AddressOfEntryPoint
> +	.long	stext - efi_head		// BaseOfCode
> +
> +extra_header_fields:
> +	.quad	0				// ImageBase
> +	.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 - efi_head		// SizeOfImage
> +
> +	// Everything before the kernel image is considered part of the header
> +	.long	stext - efi_head			// SizeOfHeaders
> +	.long	0				// CheckSum
> +	.short	0xa				// Subsystem (EFI application)
> +	.short	0				// DllCharacteristics
> +	.quad	0				// SizeOfStackReserve
> +	.quad	0				// SizeOfStackCommit
> +	.quad	0				// SizeOfHeapReserve
> +	.quad	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
> +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 - stext		// VirtualSize
> +	.long	stext - efi_head	// VirtualAddress
> +	.long	_edata - stext		// SizeOfRawData
> +	.long	stext - efi_head	// 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)
> +	.align 5
> +#endif
>  
>  ENTRY(stext)
>  	mov	x21, x0				// x21=FDT
> -- 
> 1.8.3.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-05 15:28       ` Catalin Marinas
@ 2013-12-06 12:25         ` Grant Likely
  2013-12-06 13:34           ` Mark Salter
  0 siblings, 1 reply; 24+ messages in thread
From: Grant Likely @ 2013-12-06 12:25 UTC (permalink / raw)
  To: Catalin Marinas, Mark Salter
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Thu, 5 Dec 2013 15:28:06 +0000, Catalin Marinas <catalin.marinas@arm.com> wrote:
> On Thu, Dec 05, 2013 at 02:43:23PM +0000, Mark Salter wrote:
> > On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> > > On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > > > This patch adds PE/COFF header fields to the start of the Image
> > > > so that it appears as an EFI application to EFI firmware. An EFI
> > > > stub is included to allow direct booting of the kernel Image. Due
> > > > to EFI firmware limitations, only little endian kernels with 4K
> > > > page sizes are supported at this time.
> > > 
> > > I don't fully understand the EFI firmware limitations but for big endian
> > > we could have the EFI_STUB wrapper in little endian and get the kernel
> > > to switch to big endian once booted. The image header should always be
> > > little endian.
> > 
> > That would be fun. :) You'd also have to switch back and forth to make
> > EFI runtime services calls.
> 
> OK, we'll have to live with this restriction.

Or just disable runtime services on the switch to big ending. Big endian
should not disable the stub (but getting it to work could be a follow-up
patch)

g.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-06 12:25         ` Grant Likely
@ 2013-12-06 13:34           ` Mark Salter
  2013-12-06 13:38             ` Leif Lindholm
  0 siblings, 1 reply; 24+ messages in thread
From: Mark Salter @ 2013-12-06 13:34 UTC (permalink / raw)
  To: Grant Likely
  Cc: Catalin Marinas, linux-kernel, patches, Ard Biesheuvel,
	Will Deacon, linux-arm-kernel, matt.fleming, linux-efi,
	Leif Lindholm, roy.franz

On Fri, 2013-12-06 at 12:25 +0000, Grant Likely wrote:
> On Thu, 5 Dec 2013 15:28:06 +0000, Catalin Marinas <catalin.marinas@arm.com> wrote:
> > On Thu, Dec 05, 2013 at 02:43:23PM +0000, Mark Salter wrote:
> > > On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> > > > On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > > > > This patch adds PE/COFF header fields to the start of the Image
> > > > > so that it appears as an EFI application to EFI firmware. An EFI
> > > > > stub is included to allow direct booting of the kernel Image. Due
> > > > > to EFI firmware limitations, only little endian kernels with 4K
> > > > > page sizes are supported at this time.
> > > > 
> > > > I don't fully understand the EFI firmware limitations but for big endian
> > > > we could have the EFI_STUB wrapper in little endian and get the kernel
> > > > to switch to big endian once booted. The image header should always be
> > > > little endian.
> > > 
> > > That would be fun. :) You'd also have to switch back and forth to make
> > > EFI runtime services calls.
> > 
> > OK, we'll have to live with this restriction.
> 
> Or just disable runtime services on the switch to big ending. Big endian
> should not disable the stub (but getting it to work could be a follow-up
> patch)
> 
The other problem with BE is that the PE/COFF masquerading is built into
head.S so the same Image can be used for EFI and non-EFI. I don't see
a BE opcode which we could us to provide the magic "MZ" at the start
of a BE kernel Image.

--Mark



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-06 13:34           ` Mark Salter
@ 2013-12-06 13:38             ` Leif Lindholm
  2013-12-06 13:51               ` Mark Salter
  0 siblings, 1 reply; 24+ messages in thread
From: Leif Lindholm @ 2013-12-06 13:38 UTC (permalink / raw)
  To: Mark Salter
  Cc: Grant Likely, Catalin Marinas, linux-kernel, patches,
	Ard Biesheuvel, Will Deacon, linux-arm-kernel, matt.fleming,
	linux-efi, roy.franz

On Fri, Dec 06, 2013 at 08:34:30AM -0500, Mark Salter wrote:
> > Or just disable runtime services on the switch to big ending. Big endian
> > should not disable the stub (but getting it to work could be a follow-up
> > patch)
> > 
> The other problem with BE is that the PE/COFF masquerading is built into
> head.S so the same Image can be used for EFI and non-EFI. I don't see
> a BE opcode which we could us to provide the magic "MZ" at the start
> of a BE kernel Image.

That's actually not an issue.
Instructions are always LE - endinaness affects only data.

/
    Leif

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-06 13:38             ` Leif Lindholm
@ 2013-12-06 13:51               ` Mark Salter
  0 siblings, 0 replies; 24+ messages in thread
From: Mark Salter @ 2013-12-06 13:51 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: Grant Likely, Catalin Marinas, linux-kernel, patches,
	Ard Biesheuvel, Will Deacon, linux-arm-kernel, matt.fleming,
	linux-efi, roy.franz

On Fri, 2013-12-06 at 14:38 +0100, Leif Lindholm wrote:
> On Fri, Dec 06, 2013 at 08:34:30AM -0500, Mark Salter wrote:
> > > Or just disable runtime services on the switch to big ending. Big endian
> > > should not disable the stub (but getting it to work could be a follow-up
> > > patch)
> > > 
> > The other problem with BE is that the PE/COFF masquerading is built into
> > head.S so the same Image can be used for EFI and non-EFI. I don't see
> > a BE opcode which we could us to provide the magic "MZ" at the start
> > of a BE kernel Image.
> 
> That's actually not an issue.
> Instructions are always LE - endinaness affects only data.
> 

Oh right, I knew that. Time for coffee.



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-12-05 15:25   ` Catalin Marinas
  2013-12-05 15:52     ` Mark Salter
@ 2013-12-06 14:34     ` Mark Salter
  1 sibling, 0 replies; 24+ messages in thread
From: Mark Salter @ 2013-12-06 14:34 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: linux-kernel, patches, Will Deacon, linux-arm-kernel,
	matt.fleming, linux-efi, Leif Lindholm, roy.franz

On Thu, 2013-12-05 at 15:25 +0000, Catalin Marinas wrote:
> On Fri, Nov 29, 2013 at 10:05:12PM +0000, Mark Salter wrote:
> > +
> > +#define efi_remap(cookie, size) __ioremap((cookie), (size), PAGE_KERNEL_EXEC)
> > +#define efi_ioremap(cookie, size) __ioremap((cookie), (size), \
> > +                                           __pgprot(PROT_DEVICE_nGnRE))
> > +#define efi_unmap(cookie) __iounmap((cookie))
> > +#define efi_iounmap(cookie) __iounmap((cookie))
> 
> I prefer to use the ioremap_*() functions rather than the lower-level
> __ioremap(). The ioremap_cache() should give us executable memory.

okay

> 
> Looking at the code, I think we have a bug with ioremap_cache() using
> MT_NORMAL since it doesn't have the shareability bit (Device memory does
> not require this on AArch64). PROT_DEFAULT should change to
> pgprot_default | PTE_DIRTY.
> 

ah, yes.

> > +       if (depth != 1 ||
> > +           (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
> > +               return 0;
> > +
> > +       pr_info("Getting EFI parameters from FDT.\n");
> > +
> > +       prop = of_get_flat_dt_prop(node, "linux,uefi-system-table", &len);
> > +       if (!prop) {
> > +               pr_err("No EFI system table in FDT\n");
> > +               return 0;
> > +       }
> > +       efi_system_table = of_read_ulong(prop, len/4);
> > +
> > +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-start", &len);
> > +       if (!prop) {
> > +               pr_err("No EFI memmap in FDT\n");
> > +               return 0;
> > +       }
> > +       memmap.phys_map = (void *)of_read_ulong(prop, len/4);
> > +
> > +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-size", &len);
> > +       if (!prop) {
> > +               pr_err("No EFI memmap size in FDT\n");
> > +               return 0;
> > +       }
> > +       size = of_read_ulong(prop, len/4);
> > +       memmap.map_end = memmap.phys_map + size;
> > +
> > +       /* reserve memmap */
> > +       memblock_reserve((u64)memmap.phys_map, size);
> > +
> > +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-size", &len);
> > +       if (!prop) {
> > +               pr_err("No EFI memmap descriptor size in FDT\n");
> > +               return 0;
> > +       }
> > +       memmap.desc_size = of_read_ulong(prop, len/4);
> > +
> > +       memmap.nr_map = size / memmap.desc_size;
> > +
> > +       prop = of_get_flat_dt_prop(node, "linux,uefi-mmap-desc-ver", &len);
> > +       if (!prop) {
> > +               pr_err("No EFI memmap descriptor version in FDT\n");
> > +               return 0;
> > +       }
> > +       memmap.desc_version = of_read_ulong(prop, len/4);
> > +
> > +       if (uefi_debug) {
> > +               pr_info("  EFI system table @ %p\n", (void *)efi_system_table);
> > +               pr_info("  EFI mmap @ %p-%p\n", memmap.phys_map,
> > +                       memmap.map_end);
> > +               pr_info("  EFI mmap descriptor size = 0x%lx\n",
> > +                       memmap.desc_size);
> > +               pr_info("  EFI mmap descriptor version = 0x%lx\n",
> > +                       memmap.desc_version);
> > +       }
> > +
> > +       return 1;
> > +}
> 
> Are these checks generic to other architectures? We may do with some
> helpers to avoid duplication.

That whole function could probably be shared.

> 
> > +
> > +#define PGD_END  (&idmap_pg_dir[sizeof(idmap_pg_dir)/sizeof(pgd_t)])
> 
> Just &idmap_pg_dir[PTRS_PER_PGD] would do (or idmap_pg_dir +
> ARRAY_SIZE(idmap_pg_dir)).

Should have been ARRAY_SIZE. I didn't want to use PTRS_PER_PGD in case
that changed.

> 
> > +#ifndef CONFIG_SMP
> > +#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF)
> > +#else
> > +#define PMD_FLAGS  (PMD_ATTRINDX(MT_NORMAL) | PMD_TYPE_SECT | PMD_SECT_AF | \
> > +                   PMD_SECT_S)
> > +#endif
> > +
> > +static void __init create_idmap(unsigned long addr, unsigned long len)
> 
> I would change this to use the existing create_mapping() function which
> takes care of allocating pud/pmd/ptes. We shouldn't duplicate this
> functionality in two places. create_mapping() may need a slight change
> since it assumes swapper_pg_dir but it's not much. It also uses
> memblock_alloc() for early allocations.

I'll take a look at this.

> 
> > +static __init int memmap_walk(struct efi_memory_map *memmap,
> > +                             int (*func)(efi_memory_desc_t *, void *),
> > +                             void *private_data, int early)
> 
> Is this generic enough as a common helper between arm and arm64 (and
> maybe x86)?

Probably not as-is, but one could be made. 
> 
> > +static __init int reserve_region(efi_memory_desc_t *md, void *priv)
> [...]
> > +static __init void reserve_regions(void)
> [...]
> > +static int __init remap_region(efi_memory_desc_t *md, void *priv)
> [...]
> > +static int __init remap_regions(efi_memory_desc_t *map)
> 
> Same here (I haven't looked at the other implementations).

Only in arm/arm64 patches and they are different.

> 
> > +/*
> > + * Called from setup_arch with interrupts disabled.
> > + */
> > +void __init efi_enter_virtual_mode(void)
> > +{
> > +       void *newmap;
> > +       efi_status_t status;
> > +       u64 mapsize, total_freed = 0;
> > +       int count;
> > +
> > +       if (!efi_enabled(EFI_BOOT)) {
> > +               pr_info("EFI services will not be available.\n");
> > +               return;
> > +       }
> > +
> > +       pr_info("Remapping and enabling EFI services.\n");
> > +
> > +       mapsize = memmap.map_end - memmap.phys_map;
> > +       memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
> > +                                                  mapsize);
> > +       memmap.map_end = memmap.map + mapsize;
> > +
> > +       /* Map the regions we reserved earlier */
> > +       newmap = alloc_bootmem(mapsize);
> 
> memblock_alloc() (also check the other bootmem uses in this patch, arm64
> is using memblock).

Okay, the bootmem function do use the memblock interface with some leak
detection added, but I can use memblock directly.

> 
> > +       if (newmap == NULL) {
> > +               pr_err("Failed to allocate new EFI memmap\n");
> > +               return;
> > +       }
> > +
> > +       count = remap_regions(newmap);
> > +       if (count <= 0) {
> > +               pr_err("Failed to remap EFI regions.\n");
> > +               return;
> > +       }
> > +
> > +       efi.memmap = &memmap;
> > +
> > +       efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
> > +       if (efi.systab)
> > +               set_bit(EFI_SYSTEM_TABLES, &arm_efi_facility);
> > +
> > +       /*
> > +        * efi.systab->runtime is a pointer to something guaranteed by
> > +        * the UEFI specification to be 1:1 mapped.
> > +        */
> > +       runtime = (__force void *)efi_lookup_mapped_addr((u64)efi.systab->runtime);
> > +
> > +       /* Call SetVirtualAddressMap with the physical address of the map */
> > +       efi.set_virtual_address_map = runtime->set_virtual_address_map;
> > +
> > +       /* boot time idmap_pg_dir is incomplete, so fill in missing parts */
> > +       efi_setup_idmap();
> > +
> > +       cpu_switch_mm(idmap_pg_dir, &init_mm);
> > +       flush_tlb_all();
> > +       flush_cache_all();
> > +
> > +       status = efi.set_virtual_address_map(count * memmap.desc_size,
> > +                                            memmap.desc_size,
> > +                                            memmap.desc_version,
> > +                                            newmap);
> > +       cpu_set_reserved_ttbr0();
> > +       flush_tlb_all();
> > +       flush_cache_all();
> 
> What is the point of cache flusing here?

Paranoia based on not knowing what the firmware will be caching.
If it doesn't matter and there's nothing to worry about I can drop
it. 



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-05 14:18   ` Catalin Marinas
  2013-12-05 14:43     ` Mark Salter
@ 2013-12-06 14:55     ` Mark Salter
  2013-12-16 15:46       ` Catalin Marinas
  1 sibling, 1 reply; 24+ messages in thread
From: Mark Salter @ 2013-12-06 14:55 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> Hi Mark,
> 
> On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > +#include <linux/linkage.h>
> > +#include <linux/init.h>
> > +
> > +#include <asm/assembler.h>
> > +
> > +#define EFI_LOAD_ERROR 0x8000000000000001
> 
> It's defined already but I see why you can't include efi.h here. Maybe a
> comment.

okay

> 
> > +
> > +       __INIT
> > +
> > +       /*
> > +        * We arrive here from the EFI boot manager with:
> > +        *
> > +        *    * MMU on with identity-mapped RAM.
> > +        *    * Icache and Dcache on
> > +        *
> > +        * We will most likely be running from some place other than where
> > +        * we want to be. The kernel image wants to be placed at TEXT_OFFSET
> > +        * from start of RAM.
> > +        */
> > +ENTRY(efi_stub_entry)
> > +       stp     x29, x30, [sp, #-32]!
> > +
> > +       /*
> > +        * Call efi_entry to do the real work.
> > +        * x0 and x1 are already set up by firmware. Current runtime
> > +        * address of image is calculated and passed via *image_addr.
> > +        *
> > +        * unsigned long efi_entry(void *handle,
> > +        *                         efi_system_table_t *sys_table,
> > +        *                         unsigned long *image_addr) ;
> > +        */
> > +       adrp    x8, _text
> > +        add    x8, x8, #:lo12:_text
> 
> Minor: some wrong whitespace (but I don't trust our incoming mail server
> either, it corrupts patches usually).

I will fix it. (the whitespace, not your mail server)

> 
> > +       add     x2, sp, 16
> > +       str     x8, [x2]
> > +       bl      efi_entry
> > +       cmn     x0, #1
> > +       b.eq    efi_load_fail
> > +
> > +       /*
> > +        * efi_entry() will have relocated the kernel image if necessary
> > +        * and we return here with device tree address in x0 and the kernel
> > +        * entry point stored at *image_addr. Save those values in registers
> > +        * which are preserved by __flush_dcache_all.
> > +        */
> > +       ldr     x1, [sp, #16]
> > +       mov     x20, x0
> > +       mov     x21, x1
> > +
> > +       bl      __flush_dcache_all
> 
> Regarding __flush_dcache_all, I plan to remove it for all cases apart
> from power management with the MMU disabled. With MMU enabled, there is
> no guarantee that this function does the right thing. It's even worse in
> the guest context.

According to booting.txt, the dcache needs to be invalidated. Is there
something existing I can use or do I need to write it?
> 
> > +       /* Turn off Dcache and MMU */
> > +       mrs     x0, sctlr_el1
> > +       bic     x0, x0, #1 << 0 // clear SCTLR.M
> > +       bic     x0, x0, #1 << 2 // clear SCTLR.C
> > +       msr     sctlr_el1, x0
> > +       isb
> 
> I assume an EFI app is running with the MMU enabled (and UP only). Do we
> always run it in EL1? What about EL2 mode (needed by KVM and Xen)?

Good point. It could be non-secure EL2.

> 
> > +
> > +       /* Jump to real entry point */
> > +       mov     x0, x20
> > +       mov     x1, xzr
> > +       mov     x2, xzr
> > +       mov     x3, xzr
> > +       br      x21
> > +
> > +efi_load_fail:
> > +       mov     x0, EFI_LOAD_ERROR
> 
> Needs #EFI_LOAD_ERROR (strange that gas doesn't complain).

Hmm, no complaint but it DTRT.

> > +/*
> > + * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from
> > + * start of kernel and may not cross a 2MiB boundary. We set alignment to
> > + * equal max size so we know it won't cross a 2MiB boudary.
> > + */
> > +#define MAX_DTB_SIZE   0x40000
> 
> 2MB is 0x200000 (or I don't understand the comment).

I had a little trouble with it myself. :) The size was left over from
older code which used it directly in an allocation. I'll fix the
comment, drop MAX_DTB_SIZE, and fix DTB_ALIGN to be 2MiB.

> > +
> > +static unsigned long __init get_dram_base(efi_system_table_t *sys_table)
> > +{
> > +       efi_status_t status;
> > +       unsigned long map_size, desc_size;
> > +       unsigned long membase = EFI_ERROR;
> > +       efi_memory_desc_t *memory_map;
> > +       int i;
> > +
> > +       status = efi_get_memory_map(sys_table, &memory_map, &map_size,
> > +                                   &desc_size, NULL, NULL);
> > +       if (status == EFI_SUCCESS) {
> 
> Can you exit earlier here if !EFI_SUCCESS? It reduces the indentation
> level.
> 
Yes.



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-11-29 22:05 ` [PATCH 3/3] arm64: add EFI runtime services Mark Salter
  2013-12-05 15:25   ` Catalin Marinas
@ 2013-12-09 13:52   ` Leif Lindholm
  2013-12-10 17:58     ` Mark Salter
  1 sibling, 1 reply; 24+ messages in thread
From: Leif Lindholm @ 2013-12-09 13:52 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Catalin Marinas, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, roy.franz

Apologies for late feedback.

On Fri, Nov 29, 2013 at 05:05:12PM -0500, Mark Salter wrote:
> diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> new file mode 100644
> index 0000000..1bad8a7
> --- /dev/null
> +++ b/arch/arm64/kernel/efi.c
> @@ -0,0 +1,507 @@
> +/*
> + * Extensible Firmware Interface
> + *
> + * Based on Extensible Firmware Interface Specification version 2.3.1

Actually, for arm64, we're relying on UEFI 2.4 or later.

> + *
> + * Copyright (C) 2013 Linaro Ltd.

And I'd say you've easily done enough here to motivate adding Red Hat.

> + *
> + * Adapted for arm64 from arch/arm/kernel/efi.c code
> + */
> +
> +#include <linux/efi.h>
> +#include <linux/export.h>
> +#include <linux/memblock.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/bootmem.h>
> +
> +#include <asm/cacheflush.h>
> +#include <asm/efi.h>
> +#include <asm/tlbflush.h>
> +#include <asm/mmu_context.h>
> +
> +#define efi_early_remap(a, b) \
> +	((__force void *)early_ioremap((a), (b)))

Do we not want early_memremap() here, rather than early_ioremap()?

/
    Leif

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/3] arm64: add EFI runtime services
  2013-12-09 13:52   ` Leif Lindholm
@ 2013-12-10 17:58     ` Mark Salter
  0 siblings, 0 replies; 24+ messages in thread
From: Mark Salter @ 2013-12-10 17:58 UTC (permalink / raw)
  To: Leif Lindholm
  Cc: linux-kernel, patches, Catalin Marinas, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, roy.franz

On Mon, 2013-12-09 at 14:52 +0100, Leif Lindholm wrote:
> Apologies for late feedback.
> 
> On Fri, Nov 29, 2013 at 05:05:12PM -0500, Mark Salter wrote:
> > diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
> > new file mode 100644
> > index 0000000..1bad8a7
> > --- /dev/null
> > +++ b/arch/arm64/kernel/efi.c
> > @@ -0,0 +1,507 @@
> > +/*
> > + * Extensible Firmware Interface
> > + *
> > + * Based on Extensible Firmware Interface Specification version 2.3.1
> 
> Actually, for arm64, we're relying on UEFI 2.4 or later.

You're right.

> 
> > + *
> > + * Copyright (C) 2013 Linaro Ltd.
> 
> And I'd say you've easily done enough here to motivate adding Red Hat.
> 
> > + *
> > + * Adapted for arm64 from arch/arm/kernel/efi.c code
> > + */
> > +
> > +#include <linux/efi.h>
> > +#include <linux/export.h>
> > +#include <linux/memblock.h>
> > +#include <linux/of.h>
> > +#include <linux/of_fdt.h>
> > +#include <linux/sched.h>
> > +#include <linux/slab.h>
> > +#include <linux/bootmem.h>
> > +
> > +#include <asm/cacheflush.h>
> > +#include <asm/efi.h>
> > +#include <asm/tlbflush.h>
> > +#include <asm/mmu_context.h>
> > +
> > +#define efi_early_remap(a, b) \
> > +	((__force void *)early_ioremap((a), (b)))
> 
> Do we not want early_memremap() here, rather than early_ioremap()?
> 

Yes.



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/3] arm64: add EFI stub
  2013-12-06 14:55     ` Mark Salter
@ 2013-12-16 15:46       ` Catalin Marinas
  0 siblings, 0 replies; 24+ messages in thread
From: Catalin Marinas @ 2013-12-16 15:46 UTC (permalink / raw)
  To: Mark Salter
  Cc: linux-kernel, patches, Ard Biesheuvel, Will Deacon,
	linux-arm-kernel, matt.fleming, linux-efi, Leif Lindholm,
	roy.franz

On Fri, Dec 06, 2013 at 02:55:54PM +0000, Mark Salter wrote:
> On Thu, 2013-12-05 at 14:18 +0000, Catalin Marinas wrote:
> > On Fri, Nov 29, 2013 at 10:05:10PM +0000, Mark Salter wrote:
> > > +       add     x2, sp, 16
> > > +       str     x8, [x2]
> > > +       bl      efi_entry
> > > +       cmn     x0, #1
> > > +       b.eq    efi_load_fail
> > > +
> > > +       /*
> > > +        * efi_entry() will have relocated the kernel image if necessary
> > > +        * and we return here with device tree address in x0 and the kernel
> > > +        * entry point stored at *image_addr. Save those values in registers
> > > +        * which are preserved by __flush_dcache_all.
> > > +        */
> > > +       ldr     x1, [sp, #16]
> > > +       mov     x20, x0
> > > +       mov     x21, x1
> > > +
> > > +       bl      __flush_dcache_all
> > 
> > Regarding __flush_dcache_all, I plan to remove it for all cases apart
> > from power management with the MMU disabled. With MMU enabled, there is
> > no guarantee that this function does the right thing. It's even worse in
> > the guest context.
> 
> According to booting.txt, the dcache needs to be invalidated. Is there
> something existing I can use or do I need to write it?

The function will stay for a few cases where needed. But here the
D-cache and MMU are still on at this point and there is a slight chance
of speculative loads after the flush (though only clean lines). You
could move this after he MMU disabling (but still keep the I-cache on
for performance).

-- 
Catalin

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2013-12-16 15:46 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-29 22:05 [PATCH 0/3] arm64: Add EFI stub and runtime services support Mark Salter
     [not found] ` < 1385762712-17043-2-git-send-email-msalter@redhat.com>
2013-11-29 22:05 ` [PATCH 1/3] arm64: add EFI stub Mark Salter
2013-12-03 18:38   ` Will Deacon
2013-12-03 19:31     ` Roy Franz
2013-12-03 19:31     ` Mark Salter
2013-12-05 14:18   ` Catalin Marinas
2013-12-05 14:43     ` Mark Salter
2013-12-05 15:28       ` Catalin Marinas
2013-12-06 12:25         ` Grant Likely
2013-12-06 13:34           ` Mark Salter
2013-12-06 13:38             ` Leif Lindholm
2013-12-06 13:51               ` Mark Salter
2013-12-06 14:55     ` Mark Salter
2013-12-16 15:46       ` Catalin Marinas
2013-12-06 12:12   ` Grant Likely
2013-11-29 22:05 ` [PATCH 2/3] doc: arm64: add description of EFI stub support Mark Salter
2013-12-05 12:53   ` Grant Likely
2013-11-29 22:05 ` [PATCH 3/3] arm64: add EFI runtime services Mark Salter
2013-12-05 15:25   ` Catalin Marinas
2013-12-05 15:52     ` Mark Salter
2013-12-05 15:59       ` Catalin Marinas
2013-12-06 14:34     ` Mark Salter
2013-12-09 13:52   ` Leif Lindholm
2013-12-10 17:58     ` Mark Salter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).