2014-02-05 17:05:13

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 00/22] arm/arm64: UEFI stubs + runtime services

This is a combined set of patches for the arm and arm64 kernel
support for UEFI firmware.

The set depends on the following prerequisite sets:
- Laura Abbott's 'Remove ARM meminfo'
- Matt Fleming's 'Move facility flags to struct efi'
- Mark Salter's 'Generic fixmap'
- Mark Salter's 'Generic early_ioremap'
- Mark Salter's 'EFI memory map iteration helper'

All of these are held on top of 3.14-rc1, with the patches in this set,
in git://git.linaro.org/people/leif.lindholm/linux.git, rebasing branch
uefi-for-upstream.

New in this version:
- Much more stub code shared arm/arm64.
- Stub looks in configuration tables for DT.
- Stub creates empty DT if none found/specified.
- Stub ignores dtb= option when UEFI Secure Boot active.
- Stub deletes any memory nodes in DT.
- Kernel sets up available RAM for memblock from UEFI memory map.
(So no longer need different DTs or command line parameters for
otherwise identical platforms with different amounts of RAM.)
- arm64 now compatible with 64K pages (with 4K UEFI page size).
- Fix mapping of runtime mmio regions on arm64.
- Reworked update_sctlr asm macro.
- Various cleanup and style fixes.

---

Ard Biesheuvel (1):
arm: efistub: ignore dtb= when UEFI SecureBoot is enabled

H. Peter Anvin (1):
Improve cmdline conversion

Leif Lindholm (6):
arm: break part of __soft_restart out into separate function
arm: add new asm macro update_sctlr
Documentation: arm: add UEFI support documentation
arm: Add [U]EFI runtime services support
init: efi: arm: enable (U)EFI runtime services on arm
arm: update boot/compressed/.gitignore

Mark Salter (6):
efi: add helper function to get UEFI params from FDT
lib: add fdt_empty_tree.c
arm64: Add function to create identity mappings
arm64: add EFI stub
doc: arm64: add description of EFI stub support
arm64: add EFI runtime services

Roy Franz (8):
efi-stub.txt updates for ARM
Add shared printk wrapper for consistent prefixing
Add helper functions used by arm/arm64
Add shared FDT related functions for ARM/ARM64
Add strstr to compressed string.c for ARM.
Add shared arm/arm64 EFI stub
Add EFI stub for ARM
Disable stack protection for decompressor/stub

Documentation/arm/00-INDEX | 3 +
Documentation/arm/uefi.txt | 64 +++++
Documentation/arm64/booting.txt | 4 +
Documentation/efi-stub.txt | 33 ++-
arch/arm/Kconfig | 27 ++
arch/arm/boot/compressed/.gitignore | 3 +
arch/arm/boot/compressed/Makefile | 19 +-
arch/arm/boot/compressed/efi-header.S | 117 ++++++++
arch/arm/boot/compressed/efi-stub.c | 118 ++++++++
arch/arm/boot/compressed/efi-stub.h | 5 +
arch/arm/boot/compressed/head.S | 83 +++++-
arch/arm/boot/compressed/string.c | 21 ++
arch/arm/include/asm/assembler.h | 14 +
arch/arm/include/asm/idmap.h | 1 +
arch/arm/include/asm/uefi.h | 28 ++
arch/arm/kernel/Makefile | 2 +
arch/arm/kernel/process.c | 12 +-
arch/arm/kernel/setup.c | 7 +-
arch/arm/kernel/uefi.c | 413 ++++++++++++++++++++++++++++
arch/arm/kernel/uefi_phys.S | 69 +++++
arch/arm/mm/idmap.c | 15 ++
arch/arm64/Kconfig | 26 ++
arch/arm64/include/asm/efi.h | 12 +
arch/arm64/include/asm/mmu.h | 2 +
arch/arm64/kernel/Makefile | 4 +
arch/arm64/kernel/efi-entry.S | 93 +++++++
arch/arm64/kernel/efi-stub.c | 67 +++++
arch/arm64/kernel/efi.c | 462 ++++++++++++++++++++++++++++++++
arch/arm64/kernel/head.S | 112 ++++++++
arch/arm64/kernel/setup.c | 3 +
arch/arm64/mm/mmu.c | 66 +++--
arch/x86/boot/compressed/eboot.c | 3 +-
drivers/firmware/efi/Kconfig | 7 +
drivers/firmware/efi/arm-stub.c | 146 ++++++++++
drivers/firmware/efi/efi-stub-helper.c | 199 +++++++++++---
drivers/firmware/efi/efi.c | 79 ++++++
drivers/firmware/efi/fdt.c | 268 ++++++++++++++++++
include/linux/efi.h | 12 +
init/main.c | 5 +
lib/Makefile | 3 +-
lib/fdt_empty_tree.c | 2 +
41 files changed, 2534 insertions(+), 95 deletions(-)
create mode 100644 Documentation/arm/uefi.txt
create mode 100644 arch/arm/boot/compressed/efi-header.S
create mode 100644 arch/arm/boot/compressed/efi-stub.c
create mode 100644 arch/arm/boot/compressed/efi-stub.h
create mode 100644 arch/arm/include/asm/uefi.h
create mode 100644 arch/arm/kernel/uefi.c
create mode 100644 arch/arm/kernel/uefi_phys.S
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
create mode 100644 drivers/firmware/efi/arm-stub.c
create mode 100644 drivers/firmware/efi/fdt.c
create mode 100644 lib/fdt_empty_tree.c

--
1.7.10.4


2014-02-05 17:05:24

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 07/22] Add shared FDT related functions for ARM/ARM64

From: Roy Franz <[email protected]>

Both ARM and ARM64 stubs will update the device tree that they pass to
the kernel. In both cases they primarily need to add the same UEFI
related information, so the function can be shared. Create a new FDT
related file for this to avoid use of architecture #ifdefs in
efi-stub-helper.c.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
---
drivers/firmware/efi/fdt.c | 247 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 3 +
2 files changed, 250 insertions(+)
create mode 100644 drivers/firmware/efi/fdt.c

diff --git a/drivers/firmware/efi/fdt.c b/drivers/firmware/efi/fdt.c
new file mode 100644
index 0000000..a602b0a
--- /dev/null
+++ b/drivers/firmware/efi/fdt.c
@@ -0,0 +1,247 @@
+/*
+ * FDT related Helper functions used by the EFI stub on multiple
+ * architectures. This should be #included by the EFI stub
+ * implementation files.
+ *
+ * Copyright 2013 Linaro Limited; author Roy Franz
+ *
+ * This file is part of the Linux kernel, and is made available
+ * under the terms of the GNU General Public License version 2.
+ *
+ */
+
+static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
+ void *fdt, int new_fdt_size, char *cmdline_ptr,
+ u64 initrd_addr, u64 initrd_size,
+ efi_memory_desc_t *memory_map,
+ unsigned long map_size, unsigned long desc_size,
+ u32 desc_ver)
+{
+ int node;
+ int status;
+ u32 fdt_val32;
+ u64 fdt_val64;
+
+ /*
+ * Copy definition of linux_banner here. Since this code is
+ * built as part of the decompressor for ARM v7, pulling
+ * in version.c where linux_banner is defined for the
+ * kernel brings other kernel dependencies with it.
+ */
+ const char linux_banner[] =
+ "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
+ LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
+
+ status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+ if (status != 0)
+ goto fdt_set_fail;
+
+ /* Delete any memory nodes present */
+ while ((node = fdt_subnode_offset(fdt, 0, "memory")) >= 0)
+ fdt_del_node(fdt, node);
+
+ node = fdt_subnode_offset(fdt, 0, "chosen");
+ if (node < 0) {
+ node = fdt_add_subnode(fdt, 0, "chosen");
+ if (node < 0) {
+ status = node; /* node is error code when negative */
+ goto fdt_set_fail;
+ }
+ }
+
+ if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
+ status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
+ strlen(cmdline_ptr) + 1);
+ if (status)
+ goto fdt_set_fail;
+ }
+
+ /* Set initrd address/end in device tree, if present */
+ if (initrd_size != 0) {
+ u64 initrd_image_end;
+ u64 initrd_image_start = cpu_to_fdt64(initrd_addr);
+ status = fdt_setprop(fdt, node, "linux,initrd-start",
+ &initrd_image_start, sizeof(u64));
+ if (status)
+ goto fdt_set_fail;
+ initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
+ status = fdt_setprop(fdt, node, "linux,initrd-end",
+ &initrd_image_end, sizeof(u64));
+ if (status)
+ goto fdt_set_fail;
+ }
+
+ /* Add FDT entries for EFI runtime services in chosen node. */
+ node = fdt_subnode_offset(fdt, 0, "chosen");
+ fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table);
+ status = fdt_setprop(fdt, node, "linux,uefi-system-table",
+ &fdt_val64, sizeof(fdt_val64));
+ if (status)
+ goto fdt_set_fail;
+
+ fdt_val64 = cpu_to_fdt64((u64)(unsigned long)memory_map);
+ status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
+ &fdt_val64, sizeof(fdt_val64));
+ if (status)
+ goto fdt_set_fail;
+
+ fdt_val32 = cpu_to_fdt32(map_size);
+ status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
+ &fdt_val32, sizeof(fdt_val32));
+ if (status)
+ goto fdt_set_fail;
+
+ fdt_val32 = cpu_to_fdt32(desc_size);
+ status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
+ &fdt_val32, sizeof(fdt_val32));
+ if (status)
+ goto fdt_set_fail;
+
+ fdt_val32 = cpu_to_fdt32(desc_ver);
+ status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
+ &fdt_val32, sizeof(fdt_val32));
+ if (status)
+ goto fdt_set_fail;
+
+ /*
+ * Add kernel version banner so stub/kernel match can be
+ * verified.
+ */
+ status = fdt_setprop_string(fdt, node, "linux,uefi-stub-kern-ver",
+ linux_banner);
+ if (status)
+ goto fdt_set_fail;
+
+ return EFI_SUCCESS;
+
+fdt_set_fail:
+ if (status == -FDT_ERR_NOSPACE)
+ return EFI_BUFFER_TOO_SMALL;
+
+ return EFI_LOAD_ERROR;
+}
+
+#ifndef EFI_FDT_ALIGN
+#define EFI_FDT_ALIGN EFI_PAGE_SIZE
+#endif
+
+/*
+ * Allocate memory for a new FDT, then add EFI, commandline, and
+ * initrd related fields to the FDT. This routine increases the
+ * FDT allocation size until the allocated memory is large
+ * enough. EFI allocations are in EFI_PAGE_SIZE granules,
+ * which are fixed at 4K bytes, so in most cases the first
+ * allocation should succeed.
+ * EFI boot services are exited at the end of this function.
+ * There must be no allocations between the get_memory_map()
+ * call and the exit_boot_services() call, so the exiting of
+ * boot services is very tightly tied to the creation of the FDT
+ * with the final memory map in it.
+ */
+
+efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
+ void *handle,
+ unsigned long *new_fdt_addr,
+ unsigned long max_addr,
+ u64 initrd_addr, u64 initrd_size,
+ char *cmdline_ptr,
+ unsigned long fdt_addr,
+ unsigned long fdt_size)
+{
+ unsigned long map_size, desc_size;
+ u32 desc_ver;
+ unsigned long mmap_key;
+ efi_memory_desc_t *memory_map;
+ unsigned long new_fdt_size;
+ efi_status_t status;
+
+ /*
+ * 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.
+ */
+ new_fdt_size = fdt_size + EFI_PAGE_SIZE;
+ while (1) {
+ status = efi_high_alloc(sys_table, new_fdt_size, EFI_FDT_ALIGN,
+ new_fdt_addr, max_addr);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n");
+ goto fail;
+ }
+
+ /*
+ * Now that we have done our final memory allocation (and free)
+ * 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(sys_table, "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)
+ return status;
+
+ pr_efi_err(sys_table, "Exit boot services failed.\n");
+
+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:
+ return EFI_LOAD_ERROR;
+}
+
+static void *get_fdt(efi_system_table_t *sys_table)
+{
+ efi_guid_t fdt_guid = EFI_DEVICE_TREE_GUID;
+ efi_config_table_t *tables;
+ void *fdt;
+ int i;
+
+ tables = (efi_config_table_t *) sys_table->tables;
+ fdt = NULL;
+
+ for (i = 0; i < sys_table->nr_tables; i++)
+ if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) {
+ fdt = (void *) tables[i].table;
+ break;
+ }
+
+ return fdt;
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index d450673..cd112bb 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -394,6 +394,9 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si
#define EFI_FILE_SYSTEM_GUID \
EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )

+#define EFI_DEVICE_TREE_GUID \
+ EFI_GUID( 0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 )
+
typedef struct {
efi_guid_t guid;
u64 table;
--
1.7.10.4

2014-02-05 17:05:32

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 10/22] Add EFI stub for ARM

From: Roy Franz <[email protected]>

This patch adds EFI stub support for the ARM Linux kernel. The EFI stub
operates similarly to the x86 stub: it is a shim between the EFI firmware
and the normal zImage entry point, and sets up the environment that the
zImage is expecting. This includes loading the initrd (optionaly) and
device tree from the system partition based on the kernel command line.
The stub updates the device tree as necessary, adding entries for EFI
runtime services. The PE/COFF "MZ" header at offset 0 results in the
first instruction being an add that corrupts r5, which is not used by
the zImage interface.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
---
arch/arm/Kconfig | 11 +++
arch/arm/boot/compressed/Makefile | 17 ++++-
arch/arm/boot/compressed/efi-header.S | 117 ++++++++++++++++++++++++++++++++
arch/arm/boot/compressed/efi-stub.c | 118 +++++++++++++++++++++++++++++++++
arch/arm/boot/compressed/efi-stub.h | 5 ++
arch/arm/boot/compressed/head.S | 83 +++++++++++++++++++++--
6 files changed, 342 insertions(+), 9 deletions(-)
create mode 100644 arch/arm/boot/compressed/efi-header.S
create mode 100644 arch/arm/boot/compressed/efi-stub.c
create mode 100644 arch/arm/boot/compressed/efi-stub.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ef2aa77..a693921 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1885,6 +1885,17 @@ config EARLY_IOREMAP
the same virtual memory range as kmap so all early mappings must
be unapped before paging_init() is called.

+config EFI_STUB
+ bool "EFI stub support"
+ depends on EFI && !CPU_BIG_ENDIAN
+ ---help---
+ This kernel feature allows a zImage to be loaded directly by EFI
+ firmware without the use of a bootloader. A PE/COFF header is
+ added to the zImage in a way that makes the binary both a Linux
+ zImage and an PE/COFF executable that can be executed directly by
+ EFI firmware.
+ See Documentation/efi-stub.txt for more information.
+
config SECCOMP
bool
prompt "Enable seccomp to safely compute untrusted bytecode"
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 68c9183..1415411 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -91,7 +91,7 @@ suffix_$(CONFIG_KERNEL_LZ4) = lz4

# Borrowed libfdt files for the ATAG compatibility mode

-libfdt := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c
+libfdt := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
libfdt_hdrs := fdt.h libfdt.h libfdt_internal.h

libfdt_objs := $(addsuffix .o, $(basename $(libfdt)))
@@ -99,11 +99,22 @@ libfdt_objs := $(addsuffix .o, $(basename $(libfdt)))
$(addprefix $(obj)/,$(libfdt) $(libfdt_hdrs)): $(obj)/%: $(srctree)/scripts/dtc/libfdt/%
$(call cmd,shipped)

-$(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o): \
+$(addprefix $(obj)/,$(libfdt_objs) atags_to_fdt.o efi-stub.o): \
$(addprefix $(obj)/,$(libfdt_hdrs))

ifeq ($(CONFIG_ARM_ATAG_DTB_COMPAT),y)
-OBJS += $(libfdt_objs) atags_to_fdt.o
+OBJS += atags_to_fdt.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(CONFIG_EFI_STUB),y)
+CFLAGS_efi-stub.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
+OBJS += efi-stub.o
+USE_LIBFDT = y
+endif
+
+ifeq ($(USE_LIBFDT),y)
+OBJS += $(libfdt_objs)
endif

targets := vmlinux vmlinux.lds \
diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
new file mode 100644
index 0000000..dbb7101
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-header.S
@@ -0,0 +1,117 @@
+@ Copyright (C) 2013 Linaro Ltd; <[email protected]>
+@
+@ This file contains the PE/COFF header that is part of the
+@ EFI stub.
+@
+
+ .org 0x3c
+ @
+ @ The PE header can be anywhere in the file, but for
+ @ simplicity we keep it together with the MSDOS header
+ @ The offset to the PE/COFF header needs to be at offset
+ @ 0x3C in the MSDOS header.
+ @ The only 2 fields of the MSDOS header that are used are this
+ @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
+ @
+ .long pe_header @ Offset to the PE header.
+
+ .align 3
+pe_header:
+ .ascii "PE"
+ .short 0
+
+coff_header:
+ .short 0x01c2 @ ARM or Thumb
+ .short 2 @ nr_sections
+ .long 0 @ TimeDateStamp
+ .long 0 @ PointerToSymbolTable
+ .long 1 @ NumberOfSymbols
+ .short section_table - optional_header @ SizeOfOptionalHeader
+ .short 0x306 @ Characteristics.
+ @ IMAGE_FILE_32BIT_MACHINE |
+ @ IMAGE_FILE_DEBUG_STRIPPED |
+ @ IMAGE_FILE_EXECUTABLE_IMAGE |
+ @ IMAGE_FILE_LINE_NUMS_STRIPPED
+
+optional_header:
+ .short 0x10b @ PE32 format
+ .byte 0x02 @ MajorLinkerVersion
+ .byte 0x14 @ MinorLinkerVersion
+
+ .long _edata - efi_stub_entry @ SizeOfCode
+
+ .long 0 @ SizeOfInitializedData
+ .long 0 @ SizeOfUninitializedData
+
+ .long efi_stub_entry @ AddressOfEntryPoint
+ .long efi_stub_entry @ BaseOfCode
+ .long 0 @ data
+
+extra_header_fields:
+ .long 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 @ SizeOfImage
+
+ @ Everything before the entry point is considered part of the header
+ .long efi_stub_entry @ SizeOfHeaders
+ .long 0 @ CheckSum
+ .short 0xa @ Subsystem (EFI application)
+ .short 0 @ DllCharacteristics
+ .long 0 @ SizeOfStackReserve
+ .long 0 @ SizeOfStackCommit
+ .long 0 @ SizeOfHeapReserve
+ .long 0 @ SizeOfHeapCommit
+ .long 0 @ LoaderFlags
+ .long 0x6 @ NumberOfRvaAndSizes
+
+ .quad 0 @ ExportTable
+ .quad 0 @ ImportTable
+ .quad 0 @ ResourceTable
+ .quad 0 @ ExceptionTable
+ .quad 0 @ CertificationTable
+ .quad 0 @ BaseRelocationTable
+ # Section table
+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 - efi_stub_entry @ VirtualSize
+ .long efi_stub_entry @ VirtualAddress
+ .long _edata - efi_stub_entry @ SizeOfRawData
+ .long efi_stub_entry @ 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)
diff --git a/arch/arm/boot/compressed/efi-stub.c b/arch/arm/boot/compressed/efi-stub.c
new file mode 100644
index 0000000..dba8037
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-stub.c
@@ -0,0 +1,118 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013 Linaro Ltd; <[email protected]>
+ *
+ * This file implements the EFI boot stub for the ARM kernel
+ *
+ * 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 <libfdt.h>
+#include <generated/compile.h>
+#include <generated/utsrelease.h>
+#include "efi-stub.h"
+
+/*
+ * The maximum uncompressed kernel size is 32 MBytes, so we will reserve
+ * that for the decompressed kernel. We have no easy way to tell what
+ * the actuall size of code + data the uncompressed kernel will use.
+ */
+#define MAX_UNCOMP_KERNEL_SIZE 0x02000000
+
+/*
+ * The kernel zImage should be located between 32 Mbytes
+ * and 128 MBytes from the base of DRAM. The min
+ * address leaves space for a maximal size uncompressed image,
+ * and the max address is due to how the zImage decompressor
+ * picks a destination address.
+ */
+#define ZIMAGE_OFFSET_LIMIT 0x08000000
+#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE
+#define MAX_FDT_OFFSET ZIMAGE_OFFSET_LIMIT
+
+/* Include shared EFI stub code, and required headers. */
+#include "../../../../drivers/firmware/efi/efi-stub-helper.c"
+#include "../../../../drivers/firmware/efi/fdt.c"
+#include "../../../drivers/firmware/efi/arm-stub.c"
+
+static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+ unsigned long *image_addr,
+ unsigned long *image_size,
+ unsigned long *reserve_addr,
+ unsigned long *reserve_size,
+ unsigned long dram_base,
+ efi_loaded_image_t *image)
+{
+ unsigned long nr_pages;
+ efi_status_t status;
+ /* Use alloc_addr to tranlsate between types */
+ efi_physical_addr_t alloc_addr;
+
+ /*
+ * Verify that the DRAM base address is compatible the the ARM
+ * boot protocol, which determines the base of DRAM by masking
+ * off the low 24 bits of the address at which the zImage is
+ * loaded at. These assumptions are made by the decompressor,
+ * before any memory map is available.
+ */
+ if (dram_base & (ZIMAGE_OFFSET_LIMIT - 1)) {
+ pr_efi_err(sys_table, "Invalid DRAM base address alignment.\n");
+ return EFI_ERROR;
+ }
+
+ /*
+ * Reserve memory for the uncompressed kernel image. This is
+ * all that prevents any future allocations from conflicting
+ * with the kernel. Since we can't tell from the compressed
+ * image how much DRAM the kernel actually uses (due to BSS
+ * size uncertainty) we allocate the maximum possible size.
+ * Do this very early, as prints can cause memory allocations
+ * that may conflict with this.
+ */
+ alloc_addr = dram_base;
+ *reserve_size = MAX_UNCOMP_KERNEL_SIZE;
+ nr_pages = round_up(*reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+ status = efi_call_phys4(sys_table->boottime->allocate_pages,
+ EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
+ nr_pages, &alloc_addr);
+ if (status != EFI_SUCCESS) {
+ *reserve_size = 0;
+ pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n");
+ return status;
+ }
+ *reserve_addr = alloc_addr;
+
+ /*
+ * Relocate the zImage, if required. ARM doesn't have a
+ * preferred address, so we set it to 0, as we want to allocate
+ * as low in memory as possible.
+ */
+ *image_size = image->image_size;
+ status = efi_relocate_kernel(sys_table, image_addr, *image_size,
+ *image_size, 0, 0);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to relocate kernel.\n");
+ efi_free(sys_table, *reserve_size, *reserve_addr);
+ *reserve_size = 0;
+ return status;
+ }
+
+ /*
+ * Check to see if we were able to allocate memory low enough
+ * in memory. The kernel determines the base of DRAM from the
+ * address at which the zImage is loaded.
+ */
+ if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
+ pr_efi_err(sys_table, "Failed to relocate kernel, no low memory available.\n");
+ efi_free(sys_table, *reserve_size, *reserve_addr);
+ *reserve_size = 0;
+ efi_free(sys_table, *image_size, *image_addr);
+ *image_size = 0;
+ return EFI_ERROR;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/arch/arm/boot/compressed/efi-stub.h b/arch/arm/boot/compressed/efi-stub.h
new file mode 100644
index 0000000..0fe9376
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-stub.h
@@ -0,0 +1,5 @@
+#ifndef _ARM_EFI_STUB_H
+#define _ARM_EFI_STUB_H
+/* Error code returned to ASM code instead of valid FDT address. */
+#define EFI_STUB_ERROR (~0)
+#endif
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 066b034..eeb394c 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -10,6 +10,7 @@
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include "efi-stub.h"

.arch armv7-a
/*
@@ -120,22 +121,93 @@
*/
.align
.arm @ Always enter in ARM state
+ .text
start:
.type start,#function
- .rept 7
+#ifdef CONFIG_EFI_STUB
+ @ Magic MSDOS signature for PE/COFF + ADD opcode
+ @ the EFI stub only supports little endian, as the EFI functions
+ @ it invokes are little endian.
+ .word 0x62805a4d
+#else
+ mov r0, r0
+#endif
+ .rept 5
mov r0, r0
.endr
- ARM( mov r0, r0 )
- ARM( b 1f )
- THUMB( adr r12, BSYM(1f) )
- THUMB( bx r12 )
+
+ adrl r12, BSYM(zimage_continue)
+ ARM( mov pc, r12 )
+ THUMB( bx r12 )
+ @ zimage_continue will be in ARM or thumb mode as configured

.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
+
+#ifdef CONFIG_EFI_STUB
+ @ Portions of the MSDOS file header must be at offset
+ @ 0x3c from the start of the file. All PE/COFF headers
+ @ are kept contiguous for simplicity.
+#include "efi-header.S"
+
+efi_stub_entry:
+ @ The EFI stub entry point is not at a fixed address, however
+ @ this address must be set in the PE/COFF header.
+ @ EFI entry point is in A32 mode, switch to T32 if configured.
+ THUMB( adr r12, BSYM(1f) )
+ THUMB( bx r12 )
THUMB( .thumb )
1:
ARM_BE8( setend be ) @ go BE8 if compiled for BE8
+ @ Save lr on stack for possible return to EFI firmware.
+ @ Don't care about fp, but need 64 bit alignment....
+ stmfd sp!, {fp, lr}
+
+ @ allocate space on stack for passing current zImage address
+ @ and for the EFI stub to return of new entry point of
+ @ zImage, as EFI stub may copy the kernel. Pointer address
+ @ is passed in r2. r0 and r1 are passed through from the
+ @ EFI firmware to efi_entry
+ adr r3, start
+ str r3, [sp, #-8]!
+ mov r2, sp @ pass pointer in r2
+ bl efi_entry
+ ldr r3, [sp], #8 @ get new zImage address from stack
+
+ @ Check for error return from EFI stub. r0 has FDT address
+ @ or EFI_STUB_ERROR error code.
+ cmp r0, #EFI_STUB_ERROR
+ beq efi_load_fail
+
+ @ Save return values of efi_entry
+ stmfd sp!, {r0, r3}
+ bl cache_clean_flush
+ bl cache_off
+ ldmfd sp!, {r0, r3}
+
+ @ Set parameters for booting zImage according to boot protocol
+ @ put FDT address in r2, it was returned by efi_entry()
+ @ r1 is FDT machine type, and r0 needs to be 0
+ mov r2, r0
+ mov r1, #0xFFFFFFFF
+ mov r0, #0
+
+ @ Branch to (possibly) relocated zImage that is in r3
+ @ Make sure we are in A32 mode, as zImage requires
+ THUMB( bx r3 )
+ ARM( mov pc, r3 )
+
+efi_load_fail:
+ @ Return EFI_LOAD_ERROR to EFI firmware on error.
+ @ Switch back to ARM mode for EFI is done based on
+ @ return address on stack in case we are in THUMB mode
+ ldr r0, =0x80000001
+ ldmfd sp!, {fp, pc} @ put lr from stack into pc
+#endif
+
+ THUMB( .thumb )
+zimage_continue:
mrs r9, cpsr
#ifdef CONFIG_ARM_VIRT_EXT
bl __hyp_stub_install @ get into SVC mode, reversibly
@@ -168,7 +240,6 @@ not_angel:
* by the linker here, but it should preserve r7, r8, and r9.
*/

- .text

#ifdef CONFIG_AUTO_ZRELADDR
@ determine final kernel image address
--
1.7.10.4

2014-02-05 17:05:47

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 21/22] arm: efistub: ignore dtb= when UEFI SecureBoot is enabled

From: Ard Biesheuvel <[email protected]>

Loading unauthenticated FDT blobs directly from storage is a security hazard,
so this should only be allowed when running with UEFI Secure Boot disabled.

Signed-off-by: Ard Biesheuvel <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
drivers/firmware/efi/arm-stub.c | 4 +++-
drivers/firmware/efi/efi-stub-helper.c | 24 ++++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c
index b505fde..c651082 100644
--- a/drivers/firmware/efi/arm-stub.c
+++ b/drivers/firmware/efi/arm-stub.c
@@ -95,7 +95,9 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,

/* Load a device tree from the configuration table, if present. */
fdt_addr = (uintptr_t)get_fdt(sys_table);
- if (!fdt_addr) {
+ if (efi_secureboot_enabled(sys_table))
+ pr_efi(sys_table, "UEFI Secure Boot is enabled, ignoring dtb= commandline option.\n");
+ else if (!fdt_addr) {
status = handle_cmdline_files(sys_table, image, cmdline_ptr,
"dtb=",
~0UL, (unsigned long *)&fdt_addr,
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 2ee69ea..6221be7 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -721,3 +721,27 @@ static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
*cmd_line_len = options_bytes;
return (char *)cmdline_addr;
}
+
+static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
+{
+ static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID;
+ static efi_char16_t const var_name[] __initconst = {
+ 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };
+
+ efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
+ unsigned long size = sizeof(u8);
+ efi_status_t status;
+ u8 val;
+
+ status = efi_call_phys5(f_getvar, (efi_char16_t *)var_name,
+ (efi_guid_t *)&var_guid, NULL, &size, &val);
+
+ switch (status) {
+ case EFI_SUCCESS:
+ return val;
+ case EFI_NOT_FOUND:
+ return 0;
+ default:
+ return 1;
+ }
+}
--
1.7.10.4

2014-02-05 17:05:45

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 20/22] Improve cmdline conversion

From: "H. Peter Anvin" <[email protected]>

Improve the conversion of the UTF-16 EFI command line
to UTF-8 for passing to the kernel.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: H. Peter Anvin <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 3 +-
drivers/firmware/efi/arm-stub.c | 3 +-
drivers/firmware/efi/efi-stub-helper.c | 89 ++++++++++++++++++++++++--------
3 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index a7677ba..feca05f 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -488,8 +488,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table)
hdr->type_of_loader = 0x21;

/* Convert unicode cmdline to ascii */
- cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
- &options_size);
+ cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size);
if (!cmdline_ptr)
goto fail;
hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c
index aefe963..b505fde 100644
--- a/drivers/firmware/efi/arm-stub.c
+++ b/drivers/firmware/efi/arm-stub.c
@@ -87,8 +87,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
* protocol. We are going to copy the command line into the
* device tree, so this can be allocated anywhere.
*/
- cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
- &cmdline_size);
+ cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size);
if (!cmdline_ptr) {
pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
goto fail_free_image;
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index 8477a72..2ee69ea 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -625,52 +625,99 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
}

/*
- * Convert the unicode UEFI command line to ASCII to pass to kernel.
+ * Get the number of UTF-8 bytes corresponding to an UTF-16 character.
+ * This overestimates for surrogates, but that is okay.
+ */
+static int efi_utf8_bytes(u16 c)
+{
+ return 1 + (c >= 0x80) + (c >= 0x800);
+}
+
+/*
+ * Convert an UTF-16 string, not necessarily null terminated, to UTF-8.
+ */
+static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
+{
+ unsigned int c;
+
+ while (n--) {
+ c = *src++;
+ if (n && c >= 0xd800 && c <= 0xdbff &&
+ *src >= 0xdc00 && *src <= 0xdfff) {
+ c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff);
+ src++;
+ n--;
+ }
+ if (c >= 0xd800 && c <= 0xdfff)
+ c = 0xfffd; /* Unmatched surrogate */
+ if (c < 0x80) {
+ *dst++ = c;
+ continue;
+ }
+ if (c < 0x800) {
+ *dst++ = 0xc0 + (c >> 6);
+ goto t1;
+ }
+ if (c < 0x10000) {
+ *dst++ = 0xe0 + (c >> 12);
+ goto t2;
+ }
+ *dst++ = 0xf0 + (c >> 18);
+ *dst++ = 0x80 + ((c >> 12) & 0x3f);
+t2:
+ *dst++ = 0x80 + ((c >> 6) & 0x3f);
+t1:
+ *dst++ = 0x80 + (c & 0x3f);
+ }
+
+ return dst;
+}
+
+/*
+ * Do proper conversion from UTF-16 to UTF-8
* Size of memory allocated return in *cmd_line_len.
* Returns NULL on error.
*/
-static char *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg,
- efi_loaded_image_t *image,
- int *cmd_line_len)
+static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
+ efi_loaded_image_t *image,
+ int *cmd_line_len)
{
- u16 *s2;
+ const u16 *s2;
u8 *s1 = NULL;
unsigned long cmdline_addr = 0;
- int load_options_size = image->load_options_size / 2; /* ASCII */
- void *options = image->load_options;
- int options_size = 0;
+ int load_options_chars = image->load_options_size / 2; /* UTF-16 */
+ const u16 *options = image->load_options;
+ int options_bytes = 0; /* UTF-8 bytes */
+ int options_chars = 0; /* UTF-16 chars */
efi_status_t status;
- int i;
u16 zero = 0;

if (options) {
s2 = options;
- while (*s2 && *s2 != '\n' && options_size < load_options_size) {
- s2++;
- options_size++;
+ while (options_chars < load_options_chars
+ && *s2 && *s2 != '\n') {
+ options_bytes += efi_utf8_bytes(*s2++);
+ options_chars++;
}
}

- if (options_size == 0) {
- /* No command line options, so return empty string*/
- options_size = 1;
+ if (!options_chars) {
+ /* No command line options, so return empty string */
options = &zero;
}

- options_size++; /* NULL termination */
+ options_bytes++; /* NUL termination */

status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr);
if (status != EFI_SUCCESS)
return NULL;

s1 = (u8 *)cmdline_addr;
- s2 = (u16 *)options;
-
- for (i = 0; i < options_size - 1; i++)
- *s1++ = *s2++;
+ s2 = (const u16 *)options;

+ s1 = efi_utf16_to_utf8(s1, s2, options_chars);
*s1 = '\0';

- *cmd_line_len = options_size;
+ *cmd_line_len = options_bytes;
return (char *)cmdline_addr;
}
--
1.7.10.4

2014-02-05 17:05:43

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 19/22] arm64: add EFI runtime services

From: Mark Salter <[email protected]>

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 <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
arch/arm64/Kconfig | 16 ++
arch/arm64/include/asm/efi.h | 12 ++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/efi.c | 462 ++++++++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/setup.c | 3 +
init/main.c | 3 +-
6 files changed, 496 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 355f87c..636df8b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -258,6 +258,20 @@ 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 OF
+ select UCS2_STRING
+ select LIBFDT
+ select UEFI_PARAMS_FROM_FDT
+ 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 OF
@@ -317,6 +331,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..a835b2c
--- /dev/null
+++ b/arch/arm64/include/asm/efi.h
@@ -0,0 +1,12 @@
+#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
+
+#endif /* _ASM_ARM64_EFI_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 0f60b45..4c5a797 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -23,6 +23,7 @@ arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.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..90ca3d1
--- /dev/null
+++ b/arch/arm64/kernel/efi.c
@@ -0,0 +1,462 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * 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/export.h>
+#include <linux/memblock.h>
+#include <linux/bootmem.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
+
+struct efi_memory_map memmap;
+
+static efi_runtime_services_t *runtime;
+
+static u64 efi_system_table;
+
+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 is_normal_ram(efi_memory_desc_t *md)
+{
+ if (md->attribute & EFI_MEMORY_WB)
+ return 1;
+ return 0;
+}
+
+static void __init efi_setup_idmap(void)
+{
+ struct memblock_region *r;
+ efi_memory_desc_t *md;
+ u64 paddr, npages, size;
+
+ for_each_memblock(memory, r)
+ create_id_mapping(r->base, r->size, 0);
+
+ /* map runtime io spaces */
+ for_each_efi_memory_desc(&memmap, md) {
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) || is_normal_ram(md))
+ continue;
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+ create_id_mapping(paddr, size, 1);
+ }
+}
+
+static int __init uefi_init(void)
+{
+ efi_char16_t *c16;
+ char vendor[100] = "unknown";
+ int i, retval;
+
+ efi.systab = early_memremap(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (efi.systab == NULL) {
+ pr_warn("Unable to map EFI system table.\n");
+ return -ENOMEM;
+ }
+
+ set_bit(EFI_BOOT, &efi.flags);
+ set_bit(EFI_64BIT, &efi.flags);
+
+ /*
+ * Verify the EFI Table
+ */
+ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+ pr_err("System table signature incorrect\n");
+ return -EINVAL;
+ }
+ if ((efi.systab->hdr.revision >> 16) < 2)
+ pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff);
+
+ /* Show what we know for posterity */
+ c16 = early_memremap(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, &efi.flags);
+
+ early_memunmap(c16, sizeof(vendor));
+ early_memunmap(efi.systab, sizeof(efi_system_table_t));
+
+ return retval;
+}
+
+static __initdata char memory_type_name[][32] = {
+ {"Reserved"},
+ {"Loader Code"},
+ {"Loader Data"},
+ {"Boot Code"},
+ {"Boot Data"},
+ {"Runtime Code"},
+ {"Runtime Data"},
+ {"Conventional Memory"},
+ {"Unusable Memory"},
+ {"ACPI Reclaim Memory"},
+ {"ACPI Memory NVS"},
+ {"Memory Mapped I/O"},
+ {"MMIO Port Space"},
+ {"PAL Code"},
+};
+
+/*
+ * Return true for RAM regions we want to permanently reserve.
+ */
+static __init int is_reserve_region(efi_memory_desc_t *md)
+{
+ if (!is_normal_ram(md))
+ return 0;
+
+ if (md->attribute & EFI_MEMORY_RUNTIME)
+ return 1;
+
+ if (md->type == EFI_ACPI_RECLAIM_MEMORY)
+ return 1;
+
+ return 0;
+}
+
+static __init void reserve_regions(void)
+{
+ efi_memory_desc_t *md;
+ u64 paddr, npages, size;
+
+ if (uefi_debug)
+ pr_info("Processing EFI memory map:\n");
+
+ for_each_efi_memory_desc(&memmap, md) {
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+
+ if (uefi_debug)
+ pr_info(" 0x%012llx-0x%012llx [%s]",
+ paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
+ memory_type_name[md->type]);
+
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (is_normal_ram(md))
+ early_init_dt_add_memory_arch(paddr, size);
+
+ if (is_reserve_region(md) ||
+ md->type == EFI_BOOT_SERVICES_CODE ||
+ md->type == EFI_BOOT_SERVICES_DATA) {
+ memblock_reserve(paddr, size);
+ if (uefi_debug)
+ pr_cont("*");
+ }
+
+ if (uefi_debug)
+ pr_cont("\n");
+ }
+}
+
+
+static u64 __init free_one_region(u64 start, u64 end)
+{
+ u64 size = end - start;
+
+ if (uefi_debug)
+ pr_info(" EFI freeing: 0x%012llx-0x%012llx\n", start, end - 1);
+
+ free_bootmem_late(start, size);
+ return size;
+}
+
+static u64 __init free_region(u64 start, u64 end)
+{
+ u64 map_start, map_end, total = 0;
+
+ if (end <= start)
+ return total;
+
+ map_start = (u64)memmap.phys_map;
+ map_end = PAGE_ALIGN(map_start + (memmap.map_end - memmap.map));
+ map_start &= PAGE_MASK;
+
+ if (start < map_end && end > map_start) {
+ /* region overlaps UEFI memmap */
+ if (start < map_start)
+ total += free_one_region(start, map_start);
+
+ if (map_end < end)
+ total += free_one_region(map_end, end);
+ } else
+ total += free_one_region(start, end);
+
+ return total;
+}
+
+static void __init free_boot_services(void)
+{
+ u64 total_freed = 0;
+ u64 keep_end, free_start, free_end;
+ efi_memory_desc_t *md;
+
+ /*
+ * If kernel uses larger pages than UEFI, we have to be careful
+ * not to inadvertantly free memory we want to keep if there is
+ * overlap at the kernel page size alignment. We do not want to
+ * free is_reserve_region() memory nor the UEFI memmap itself.
+ *
+ * The memory map is sorted, so we keep track of the end of
+ * any previous region we want to keep, remember any region
+ * we want to free and defer freeing it until we encounter
+ * the next region we want to keep. This way, before freeing
+ * it, we can clip it as needed to avoid freeing memory we
+ * want to keep for UEFI.
+ */
+
+ keep_end = 0;
+ free_start = 0;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ u64 paddr, npages, size;
+
+ if (is_reserve_region(md)) {
+ /*
+ * We don't want to free any memory from this region.
+ */
+ if (free_start) {
+ /* adjust free_end then free region */
+ if (free_end > md->phys_addr)
+ free_end -= PAGE_SIZE;
+ total_freed += free_region(free_start, free_end);
+ free_start = 0;
+ }
+ keep_end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT);
+ continue;
+ }
+
+ if (md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA) {
+ /* no need to free this region */
+ continue;
+ }
+
+ /*
+ * We want to free memory from this region.
+ */
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (free_start) {
+ if (paddr <= free_end)
+ free_end = paddr + size;
+ else {
+ total_freed += free_region(free_start, free_end);
+ free_start = paddr;
+ free_end = paddr + size;
+ }
+ } else {
+ free_start = paddr;
+ free_end = paddr + size;
+ }
+ if (free_start < keep_end) {
+ free_start += PAGE_SIZE;
+ if (free_start >= free_end)
+ free_start = 0;
+ }
+ }
+ if (free_start)
+ total_freed += free_region(free_start, free_end);
+
+ if (total_freed)
+ pr_info("Freed 0x%llx bytes of EFI boot services memory",
+ total_freed);
+}
+
+void __init efi_init(void)
+{
+ struct efi_fdt_params params;
+
+ /* Grab UEFI information placed in FDT by stub */
+ if (!efi_get_fdt_params(&params, uefi_debug))
+ return;
+
+ efi_system_table = params.system_table;
+
+ memblock_reserve(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK)));
+ memmap.phys_map = (void *)params.mmap;
+ memmap.map = early_memremap(params.mmap, params.mmap_size);
+ memmap.map_end = memmap.map + params.mmap_size;
+ memmap.desc_size = params.desc_size;
+ memmap.desc_version = params.desc_ver;
+
+ if (uefi_init() < 0)
+ return;
+
+ reserve_regions();
+}
+
+static int __init remap_region(efi_memory_desc_t *md, void **new)
+{
+ u64 paddr, vaddr, npages, size;
+
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (is_normal_ram(md))
+ vaddr = (__force u64)ioremap_cache(paddr, size);
+ else
+ vaddr = (__force u64)ioremap(paddr, size);
+
+ if (!vaddr) {
+ pr_err("Unable to remap 0x%llx pages @ %p\n",
+ npages, (void *)paddr);
+ return 0;
+ }
+
+ /* adjust for any rounding when EFI and system pagesize differs */
+ md->virt_addr = vaddr + (md->phys_addr - paddr);
+
+ if (uefi_debug)
+ pr_info(" EFI remap 0x%012llx => %p\n",
+ md->phys_addr, (void *)md->virt_addr);
+
+ memcpy(*new, md, memmap.desc_size);
+ *new += memmap.desc_size;
+
+ return 1;
+}
+
+/*
+ * Called from setup_arch with interrupts disabled.
+ */
+void __init efi_enter_virtual_mode(void)
+{
+ efi_memory_desc_t *md;
+ phys_addr_t virtmap_phys;
+ void *virtmap, *virt_md;
+ efi_status_t status;
+ u64 mapsize;
+ int count = 0;
+ unsigned long flags;
+
+ if (!efi_enabled(EFI_BOOT)) {
+ pr_info("EFI services will not be available.\n");
+ return;
+ }
+
+ pr_info("Remapping and enabling EFI services.\n");
+
+ /* replace early memmap mapping with permanent mapping */
+ mapsize = memmap.map_end - memmap.map;
+ early_memunmap(memmap.map, mapsize);
+ memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
+ mapsize);
+ memmap.map_end = memmap.map + mapsize;
+
+ efi.memmap = &memmap;
+
+ /* Map the runtime regions */
+ virtmap = kmalloc(mapsize, GFP_KERNEL);
+ if (!virtmap) {
+ pr_err("Failed to allocate EFI virtual memmap\n");
+ return;
+ }
+ virtmap_phys = virt_to_phys(virtmap);
+ virt_md = virtmap;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (remap_region(md, &virt_md))
+ ++count;
+ }
+
+ efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table);
+ if (efi.systab)
+ set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+
+ /* boot time idmap_pg_dir is incomplete, so fill in missing parts */
+ efi_setup_idmap();
+
+ local_irq_save(flags);
+ cpu_switch_mm(idmap_pg_dir, &init_mm);
+ flush_tlb_all();
+ flush_cache_all();
+
+ /* Call SetVirtualAddressMap with the physical address of the map */
+ runtime = efi.systab->runtime;
+ efi.set_virtual_address_map = runtime->set_virtual_address_map;
+
+ status = efi.set_virtual_address_map(count * memmap.desc_size,
+ memmap.desc_size,
+ memmap.desc_version,
+ (efi_memory_desc_t *)virtmap_phys);
+ cpu_set_reserved_ttbr0();
+ flush_tlb_all();
+ flush_cache_all();
+ local_irq_restore(flags);
+
+ kfree(virtmap);
+
+ free_boot_services();
+
+ if (status != EFI_SUCCESS) {
+ pr_err("Failed to set EFI virtual address map! [%lx]\n",
+ status);
+ return;
+ }
+
+ /* Set up runtime services function pointers */
+ runtime = efi.systab->runtime;
+ 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, &efi.flags);
+}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 4d2ac74..7314460 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);
@@ -333,6 +335,7 @@ void __init setup_arch(char **cmdline_p)

parse_early_param();

+ efi_init();
arm64_memblock_init();

paging_init();
diff --git a/init/main.c b/init/main.c
index 0325962..387b5b2 100644
--- a/init/main.c
+++ b/init/main.c
@@ -903,7 +903,8 @@ static noinline void __init kernel_init_freeable(void)

do_pre_smp_initcalls();

- if (IS_ENABLED(CONFIG_ARM) && efi_enabled(EFI_BOOT))
+ if ((IS_ENABLED(CONFIG_ARM) || IS_ENABLED(CONFIG_ARM64)) &&
+ efi_enabled(EFI_BOOT))
efi_enter_virtual_mode();

lockup_detector_init();
--
1.7.10.4

2014-02-05 17:07:03

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 22/22] arm: update boot/compressed/.gitignore

Add bswapsdi2.S, fdt_empty_tree.c and fdt_sw.c to ignore list.

Signed-off-by: Leif Lindholm <[email protected]>
---
arch/arm/boot/compressed/.gitignore | 3 +++
1 file changed, 3 insertions(+)

diff --git a/arch/arm/boot/compressed/.gitignore b/arch/arm/boot/compressed/.gitignore
index 47279aa..cc3487e 100644
--- a/arch/arm/boot/compressed/.gitignore
+++ b/arch/arm/boot/compressed/.gitignore
@@ -1,4 +1,5 @@
ashldi3.S
+bswapsdi2.S
font.c
lib1funcs.S
hyp-stub.S
@@ -13,8 +14,10 @@ vmlinux.lds
# borrowed libfdt files
fdt.c
fdt.h
+fdt_empty_tree.c
fdt_ro.c
fdt_rw.c
+fdt_sw.c
fdt_wip.c
libfdt.h
libfdt_internal.h
--
1.7.10.4

2014-02-05 17:07:39

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 15/22] lib: add fdt_empty_tree.c

From: Mark Salter <[email protected]>

CONFIG_LIBFDT support does not include fdt_empty_tree.c which is
needed by arm64 EFI stub. Add it to libfdt_files.

Signed-off-by: Mark Salter <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
lib/Makefile | 3 ++-
lib/fdt_empty_tree.c | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
create mode 100644 lib/fdt_empty_tree.c

diff --git a/lib/Makefile b/lib/Makefile
index 126b34f..ce085d2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -146,7 +146,8 @@ obj-$(CONFIG_GENERIC_NET_UTILS) += net_utils.o

obj-$(CONFIG_STMP_DEVICE) += stmp_device.o

-libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o
+libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
+ fdt_empty_tree.o
$(foreach file, $(libfdt_files), \
$(eval CFLAGS_$(file) = -I$(src)/../scripts/dtc/libfdt))
lib-$(CONFIG_LIBFDT) += $(libfdt_files)
diff --git a/lib/fdt_empty_tree.c b/lib/fdt_empty_tree.c
new file mode 100644
index 0000000..5d30c58
--- /dev/null
+++ b/lib/fdt_empty_tree.c
@@ -0,0 +1,2 @@
+#include <linux/libfdt_env.h>
+#include "../scripts/dtc/libfdt/fdt_empty_tree.c"
--
1.7.10.4

2014-02-05 17:07:35

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 16/22] arm64: Add function to create identity mappings

From: Mark Salter <[email protected]>

At boot time, before switching to a virtual UEFI memory map, firmware
expects UEFI memory and IO regions to be identity mapped whenever
kernel makes runtime services calls. The exisitng early boot code
creates an identity map of kernel text/data but this is not sufficient
for UEFI. This patch adds a create_id_mapping() function which reuses
the core code of the existing create_mapping().

Signed-off-by: Mark Salter <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
arch/arm64/include/asm/mmu.h | 2 ++
arch/arm64/mm/mmu.c | 66 ++++++++++++++++++++++++++++++------------
2 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index f600d40..29ed1d8 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -28,5 +28,7 @@ extern void paging_init(void);
extern void setup_mm_for_reboot(void);
extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
extern void init_mem_pgprot(void);
+/* create an identity mapping for memory (or io if map_io is true) */
+extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io);

#endif
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 7b345e3..2eeebd1 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -168,7 +168,8 @@ static void __init *early_alloc(unsigned long sz)
}

static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
- unsigned long end, unsigned long pfn)
+ unsigned long end, unsigned long pfn,
+ pgprot_t prot)
{
pte_t *pte;

@@ -180,16 +181,28 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,

pte = pte_offset_kernel(pmd, addr);
do {
- set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
+ set_pte(pte, pfn_pte(pfn, prot));
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
}

static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
- unsigned long end, phys_addr_t phys)
+ unsigned long end, phys_addr_t phys,
+ int map_io)
{
pmd_t *pmd;
unsigned long next;
+ pmdval_t prot_sect;
+ pgprot_t prot_pte;
+
+ if (map_io) {
+ prot_sect = PMD_TYPE_SECT | PMD_SECT_AF |
+ PMD_ATTRINDX(MT_DEVICE_nGnRE);
+ prot_pte = __pgprot(PROT_DEVICE_nGnRE);
+ } else {
+ prot_sect = prot_sect_kernel;
+ prot_pte = PAGE_KERNEL_EXEC;
+ }

/*
* Check for initial section mappings in the pgd/pud and remove them.
@@ -204,22 +217,24 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
next = pmd_addr_end(addr, end);
/* try section mapping first */
if (((addr | next | phys) & ~SECTION_MASK) == 0)
- set_pmd(pmd, __pmd(phys | prot_sect_kernel));
+ set_pmd(pmd, __pmd(phys | prot_sect));
else
- alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys));
+ alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
+ prot_pte);
phys += next - addr;
} while (pmd++, addr = next, addr != end);
}

static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
- unsigned long end, unsigned long phys)
+ unsigned long end, unsigned long phys,
+ int map_io)
{
pud_t *pud = pud_offset(pgd, addr);
unsigned long next;

do {
next = pud_addr_end(addr, end);
- alloc_init_pmd(pud, addr, next, phys);
+ alloc_init_pmd(pud, addr, next, phys, map_io);
phys += next - addr;
} while (pud++, addr = next, addr != end);
}
@@ -228,30 +243,45 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
* Create the page directory entries and any necessary page tables for the
* mapping specified by 'md'.
*/
-static void __init create_mapping(phys_addr_t phys, unsigned long virt,
- phys_addr_t size)
+static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys,
+ unsigned long virt, phys_addr_t size,
+ int map_io)
{
unsigned long addr, length, end, next;
- pgd_t *pgd;
-
- if (virt < VMALLOC_START) {
- pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
- phys, virt);
- return;
- }

addr = virt & PAGE_MASK;
length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));

- pgd = pgd_offset_k(addr);
end = addr + length;
do {
next = pgd_addr_end(addr, end);
- alloc_init_pud(pgd, addr, next, phys);
+ alloc_init_pud(pgd, addr, next, phys, map_io);
phys += next - addr;
} while (pgd++, addr = next, addr != end);
}

+static void __init create_mapping(phys_addr_t phys, unsigned long virt,
+ phys_addr_t size)
+{
+ if (virt < VMALLOC_START) {
+ pr_warn("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
+ phys, virt);
+ return;
+ }
+ __create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0);
+}
+
+void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
+{
+ pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
+
+ if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
+ pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
+ return;
+ }
+ __create_mapping(pgd, addr, addr, size, map_io);
+}
+
static void __init map_mem(void)
{
struct memblock_region *reg;
--
1.7.10.4

2014-02-05 17:07:33

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 17/22] arm64: add EFI stub

From: Mark Salter <[email protected]>

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.
Support in the COFF header for signed images was provided by
Ard Biesheuvel.

Signed-off-by: Ard Biesheuvel <[email protected]>
Signed-off-by: Mark Salter <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
arch/arm64/Kconfig | 10 ++++
arch/arm64/kernel/Makefile | 3 ++
arch/arm64/kernel/efi-entry.S | 93 ++++++++++++++++++++++++++++++++++
arch/arm64/kernel/efi-stub.c | 67 ++++++++++++++++++++++++
arch/arm64/kernel/head.S | 112 +++++++++++++++++++++++++++++++++++++++++
5 files changed, 285 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 0e7b443..355f87c 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -258,6 +258,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 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 2d4554b..0f60b45 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 \
@@ -20,6 +22,7 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.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..83bfb72
--- /dev/null
+++ b/arch/arm64/kernel/efi-entry.S
@@ -0,0 +1,93 @@
+/*
+ * EFI entry point.
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ * Author: Mark Salter <[email protected]>
+ *
+ * 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
+
+ /* Turn off Dcache and MMU */
+ mrs x0, CurrentEL
+ cmp x0, #PSR_MODE_EL2t
+ ccmp x0, #PSR_MODE_EL2h, #0x4, ne
+ b.ne 1f
+ mrs x0, sctlr_el2
+ bic x0, x0, #1 << 0 // clear SCTLR.M
+ bic x0, x0, #1 << 2 // clear SCTLR.C
+ msr sctlr_el2, x0
+ isb
+ b 2f
+1:
+ 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
+2:
+ bl __flush_dcache_all
+
+ /* 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..17f5cc3
--- /dev/null
+++ b/arch/arm64/kernel/efi-stub.c
@@ -0,0 +1,67 @@
+/*
+ * linux/arch/arm/boot/compressed/efi-stub.c
+ *
+ * Copyright (C) 2013, 2014 Linaro Ltd; <[email protected]>
+ *
+ * This file implements the EFI boot stub for the arm64 kernel.
+ * Adapted from ARM version by Mark Salter <[email protected]>
+ *
+ * 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 <generated/utsrelease.h>
+
+/*
+ * 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
+ * 2MiB so we know it won't cross a 2MiB boundary.
+ */
+#define EFI_FDT_ALIGN SZ_2M /* used by allocate_new_fdt_and_exit_boot() */
+#define MAX_FDT_OFFSET SZ_512M
+
+/* Include shared EFI stub code */
+#include "../../../drivers/firmware/efi/efi-stub-helper.c"
+#include "../../../drivers/firmware/efi/fdt.c"
+#include "../../../drivers/firmware/efi/arm-stub.c"
+
+
+static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+ unsigned long *image_addr,
+ unsigned long *image_size,
+ unsigned long *reserve_addr,
+ unsigned long *reserve_size,
+ unsigned long dram_base,
+ efi_loaded_image_t *image)
+{
+ efi_status_t status;
+ unsigned long kernel_size, kernel_memsize = 0;
+
+ /* Relocate the image, if required. */
+ kernel_size = _edata - _text;
+ if (*image_addr != (dram_base + TEXT_OFFSET)) {
+ kernel_memsize = kernel_size + (_end - _edata);
+ status = efi_relocate_kernel(sys_table, image_addr,
+ kernel_size, kernel_memsize,
+ dram_base + TEXT_OFFSET,
+ PAGE_SIZE);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to relocate kernel\n");
+ return status;
+ }
+ if (*image_addr != (dram_base + TEXT_OFFSET)) {
+ pr_efi_err(sys_table, "Failed to alloc kernel memory\n");
+ efi_free(sys_table, kernel_memsize, *image_addr);
+ return EFI_ERROR;
+ }
+ *image_size = kernel_memsize;
+ }
+
+
+ return EFI_SUCCESS;
+}
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index c86bfdf..2f07979 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.7.10.4

2014-02-05 17:07:31

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 18/22] doc: arm64: add description of EFI stub support

From: Mark Salter <[email protected]>

Add explanation of arm64 EFI stub and kernel image header changes
needed to masquerade as a PE/COFF application.

Signed-off-by: Mark Salter <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
CC: [email protected]
CC: Rob Landley <[email protected]>
---
Documentation/arm64/booting.txt | 4 ++++
Documentation/efi-stub.txt | 12 +++++++++---
2 files changed, 13 insertions(+), 3 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 26be7b0..7747024 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
-tree to the kernel. This is done with the "dtb=" command line option,
+For the ARM and arm64 architectures, 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 processed in the same manner as the "initrd=" option that is
described above.
--
1.7.10.4

2014-02-05 17:08:54

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 14/22] init: efi: arm: enable (U)EFI runtime services on arm

Since the efi_set_virtual_address_map call has strict init ordering
requirements, add an explicit hook in the required place.

Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
---
init/main.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/init/main.c b/init/main.c
index 2fd9cef..0325962 100644
--- a/init/main.c
+++ b/init/main.c
@@ -902,6 +902,10 @@ static noinline void __init kernel_init_freeable(void)
smp_prepare_cpus(setup_max_cpus);

do_pre_smp_initcalls();
+
+ if (IS_ENABLED(CONFIG_ARM) && efi_enabled(EFI_BOOT))
+ efi_enter_virtual_mode();
+
lockup_detector_init();

smp_init();
--
1.7.10.4

2014-02-05 17:05:30

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 11/22] Disable stack protection for decompressor/stub

From: Roy Franz <[email protected]>

The ARM decompressor/EFI stub do not implement the functions
(__stack_chk_guard_setup, etc) that are required for support of
stack protection. The actual enablement of stack protection is
controlled by heuristics in GCC, which the code added for the EFI
stub triggers when CONFIG_STACKPROTECTOR is set. Even with
CONFIG_STACKPROTECTOR set, the decompressor was never compiled
with stack protection actually enabled. Adding -fno-stack-protector
to the decompressor/stub build keeps it building without stack
protection as it has always been built.
The x86 decompressor/stub is also built with -fno-stack-protector.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
arch/arm/boot/compressed/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 1415411..5fc32ae 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -132,7 +132,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
endif

-ccflags-y := -fpic -mno-single-pic-base -fno-builtin -I$(obj)
+ccflags-y := -fpic -mno-single-pic-base -fno-builtin -fno-stack-protector -I$(obj)
asflags-y := -DZIMAGE

# Supply kernel BSS size to the decompressor via a linker symbol.
--
1.7.10.4

2014-02-05 17:09:16

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 13/22] arm: Add [U]EFI runtime services support

This patch implements basic support for UEFI runtime services in the
ARM architecture - a requirement for using efibootmgr to read and update
the system boot configuration.

It uses the generic configuration table scanning to populate ACPI and
SMBIOS pointers.

Changes since v2:
- Updated FDT bindings.
- Preserve regions marked RESERVED (but don't map them).
- Rename 'efi' -> 'uefi' within this new port (leaving core code as is).

Signed-off-by: Leif Lindholm <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/arm/Kconfig | 16 ++
arch/arm/include/asm/uefi.h | 28 +++
arch/arm/kernel/Makefile | 2 +
arch/arm/kernel/setup.c | 7 +-
arch/arm/kernel/uefi.c | 413 +++++++++++++++++++++++++++++++++++++++++++
arch/arm/kernel/uefi_phys.S | 69 ++++++++
6 files changed, 533 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/include/asm/uefi.h
create mode 100644 arch/arm/kernel/uefi.c
create mode 100644 arch/arm/kernel/uefi_phys.S

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a693921..1e1273d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1885,6 +1885,20 @@ config EARLY_IOREMAP
the same virtual memory range as kmap so all early mappings must
be unapped before paging_init() is called.

+config EFI
+ bool "UEFI runtime service support"
+ depends on OF && !CPU_BIG_ENDIAN
+ select UCS2_STRING
+ select EARLY_IOREMAP
+ select UEFI_PARAMS_FROM_FDT
+ ---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 will
+ continue to boot on non-UEFI platforms.
+
config EFI_STUB
bool "EFI stub support"
depends on EFI && !CPU_BIG_ENDIAN
@@ -2304,6 +2318,8 @@ source "net/Kconfig"

source "drivers/Kconfig"

+source "drivers/firmware/Kconfig"
+
source "fs/Kconfig"

source "arch/arm/Kconfig.debug"
diff --git a/arch/arm/include/asm/uefi.h b/arch/arm/include/asm/uefi.h
new file mode 100644
index 0000000..3bc4c60
--- /dev/null
+++ b/arch/arm/include/asm/uefi.h
@@ -0,0 +1,28 @@
+#ifndef _ASM_ARM_EFI_H
+#define _ASM_ARM_EFI_H
+
+#ifdef CONFIG_EFI
+#include <asm/mach/map.h>
+
+extern void uefi_init(void);
+
+typedef efi_status_t uefi_phys_call_t(efi_set_virtual_address_map_t *f,
+ u32 virt_phys_offset,
+ u32 memory_map_size,
+ u32 descriptor_size,
+ u32 descriptor_version,
+ efi_memory_desc_t *dsc);
+
+extern efi_status_t uefi_phys_call(u32, u32, u32, efi_memory_desc_t *,
+ efi_set_virtual_address_map_t *);
+
+#define uefi_remap(cookie, size) __arm_ioremap((cookie), (size), MT_MEMORY_RWX)
+#define uefi_ioremap(cookie, size) __arm_ioremap((cookie), (size), MT_DEVICE)
+#define uefi_unmap(cookie) __arm_iounmap((cookie))
+#define uefi_iounmap(cookie) __arm_iounmap((cookie))
+
+#else
+#define uefi_init()
+#endif /* CONFIG_EFI */
+
+#endif /* _ASM_ARM_EFI_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a30fc9b..736cce4 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -98,4 +98,6 @@ obj-y += psci.o
obj-$(CONFIG_SMP) += psci_smp.o
endif

+obj-$(CONFIG_EFI) += uefi.o uefi_phys.o
+
extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index b074ef5..71b8839 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -30,6 +30,7 @@
#include <linux/bug.h>
#include <linux/compiler.h>
#include <linux/sort.h>
+#include <linux/efi.h>

#include <asm/unified.h>
#include <asm/cp15.h>
@@ -57,6 +58,7 @@
#include <asm/unwind.h>
#include <asm/memblock.h>
#include <asm/virt.h>
+#include <asm/uefi.h>

#include "atags.h"

@@ -878,6 +880,9 @@ void __init setup_arch(char **cmdline_p)
if (mdesc->reboot_mode != REBOOT_HARD)
reboot_mode = mdesc->reboot_mode;

+ early_ioremap_init();
+ uefi_init();
+
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
@@ -889,8 +894,6 @@ void __init setup_arch(char **cmdline_p)

parse_early_param();

- early_ioremap_init();
-
early_paging_init(mdesc, lookup_processor_type(read_cpuid_id()));
setup_dma_zone(mdesc);
sanity_check_meminfo();
diff --git a/arch/arm/kernel/uefi.c b/arch/arm/kernel/uefi.c
new file mode 100644
index 0000000..77c18b6
--- /dev/null
+++ b/arch/arm/kernel/uefi.c
@@ -0,0 +1,413 @@
+/*
+ * Based on Unified Extensible Firmware Interface Specification version 2.3.1
+ *
+ * Copyright (C) 2013-2014 Linaro Ltd.
+ *
+ * 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/export.h>
+#include <linux/memblock.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/cacheflush.h>
+#include <asm/idmap.h>
+#include <asm/setup.h>
+#include <asm/tlbflush.h>
+#include <asm/uefi.h>
+
+struct efi_memory_map memmap;
+
+static efi_runtime_services_t *runtime;
+
+static phys_addr_t uefi_system_table;
+static phys_addr_t uefi_boot_mmap;
+static u32 uefi_boot_mmap_size;
+static u32 uefi_mmap_desc_size;
+static u32 uefi_mmap_desc_ver;
+
+/*
+ * If you want to wire up a debugger and debug the UEFI side, set to 0.
+ */
+#define DISCARD_UNUSED_REGIONS 1
+
+/*
+ * If you need to (temporarily) support buggy firmware, set to 0.
+ */
+#define DISCARD_BOOT_SERVICES_REGIONS 1
+
+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 uefi_systab_init(void)
+{
+ efi_char16_t *c16;
+ char vendor[100] = "unknown";
+ int i, retval;
+
+ efi.systab = early_memremap(uefi_system_table,
+ sizeof(efi_system_table_t));
+
+ /*
+ * Verify the UEFI System Table
+ */
+ if (efi.systab == NULL)
+ panic("Whoa! Can't find UEFI system table.\n");
+ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+ panic("Whoa! UEFI system table signature incorrect\n");
+ if ((efi.systab->hdr.revision >> 16) == 0)
+ pr_warn("Warning: UEFI system table version %d.%02d, expected 2.30 or greater\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff);
+
+ /* Show what we know for posterity */
+ c16 = early_memremap(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("UEFI 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, &efi.flags);
+
+ early_memunmap(c16, sizeof(vendor));
+ early_memunmap(efi.systab, sizeof(efi_system_table_t));
+
+ return retval;
+}
+
+static __init int is_discardable_region(efi_memory_desc_t *md)
+{
+ if (md->attribute & EFI_MEMORY_RUNTIME)
+ return 0;
+
+ switch (md->type) {
+ case EFI_CONVENTIONAL_MEMORY:
+ return 1;
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ return DISCARD_BOOT_SERVICES_REGIONS;
+ /* Keep tables around for any future kexec operations */
+ case EFI_ACPI_MEMORY_NVS:
+ case EFI_ACPI_RECLAIM_MEMORY:
+ return 0;
+ /* Preserve */
+ case EFI_RESERVED_TYPE:
+ return 0;
+ }
+
+ return DISCARD_UNUSED_REGIONS;
+}
+
+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 services code"},
+ {EFI_BOOT_SERVICES_DATA, "boot services data"},
+ {EFI_RUNTIME_SERVICES_CODE, "runtime services code"},
+ {EFI_RUNTIME_SERVICES_DATA, "runtime services 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, "memory mapped I/O port space"},
+ {EFI_PAL_CODE, "pal code"},
+ {EFI_MAX_MEMORY_TYPE, NULL},
+};
+
+static __init void remove_sections(phys_addr_t addr, unsigned long size)
+{
+ unsigned long section_offset;
+ unsigned long num_sections;
+
+ section_offset = addr - (addr & SECTION_MASK);
+ num_sections = size / SECTION_SIZE;
+ if (size % SECTION_SIZE)
+ num_sections++;
+
+ memblock_remove(addr - section_offset, num_sections * SECTION_SIZE);
+}
+
+static void memmap_init(void)
+{
+ efi_memory_desc_t *md;
+ int i = 0;
+
+ if (uefi_debug)
+ pr_info("Processing UEFI memory map:\n");
+
+ memmap.map = early_memremap(uefi_boot_mmap, uefi_boot_mmap_size);
+ if (!memmap.map)
+ return;
+
+ memmap.map_end = memmap.map + uefi_boot_mmap_size;
+ memmap.nr_map = 0;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ pr_info(" %8llu pages @ %016llx (%s)\n",
+ md->num_pages, md->phys_addr,
+ memory_type_name_map[md->type].name);
+ if (md->attribute & EFI_MEMORY_WB) {
+ if (is_discardable_region(md)) {
+ arm_add_memory(md->phys_addr,
+ md->num_pages * EFI_PAGE_SIZE);
+ i++;
+ }
+ }
+ memmap.nr_map++;
+ }
+
+ if (uefi_debug)
+ pr_info("%d memory regions added.\n", i);
+
+ remove_sections(uefi_boot_mmap, uefi_boot_mmap_size);
+
+ early_memunmap(memmap.map, uefi_boot_mmap_size);
+
+ set_bit(EFI_MEMMAP, &efi.flags);
+}
+
+void __init uefi_init(void)
+{
+ struct efi_fdt_params params;
+
+ uefi_debug = 1;
+
+ /* Grab UEFI information placed in FDT by stub */
+ if (!efi_get_fdt_params(&params, uefi_debug))
+ return;
+
+ uefi_system_table = params.system_table;
+
+ uefi_boot_mmap = params.mmap;
+ uefi_boot_mmap_size = params.mmap_size;
+ uefi_mmap_desc_size = params.desc_size;
+ uefi_mmap_desc_ver = params.desc_ver;
+ memmap.desc_size = uefi_mmap_desc_size;
+ memmap.map_end = memmap.map + params.mmap_size;
+ if (uefi_boot_mmap > UINT_MAX) {
+ pr_err("UEFI memory map located above 4GB - unusable!");
+ return;
+ }
+
+ if (uefi_systab_init() < 0)
+ return;
+
+ memmap_init();
+
+ set_bit(EFI_BOOT, &efi.flags);
+}
+
+/*
+ * Disable instrrupts, enable idmap and disable caches.
+ */
+static void __init phys_call_prologue(void)
+{
+ local_irq_disable();
+
+ outer_disable();
+
+ idmap_prepare();
+}
+
+/*
+ * Restore original memory map and re-enable interrupts.
+ */
+static void __init phys_call_epilogue(void)
+{
+ static struct mm_struct *mm = &init_mm;
+
+ /* Restore original memory mapping */
+ cpu_switch_mm(mm->pgd, mm);
+
+ local_flush_bp_all();
+ local_flush_tlb_all();
+
+ outer_resume();
+
+ local_irq_enable();
+}
+
+static int __init remap_region(efi_memory_desc_t *md, int entry)
+{
+ efi_memory_desc_t *region;
+ u32 va;
+ u64 paddr;
+ u64 size;
+
+ region = memmap.map + entry * memmap.desc_size;
+ *region = *md;
+ paddr = region->phys_addr;
+ size = region->num_pages << EFI_PAGE_SHIFT;
+
+ /*
+ * Map everything writeback-capable as coherent memory,
+ * anything else as device.
+ */
+ if (md->attribute & EFI_MEMORY_WB)
+ va = (u32)uefi_remap(paddr, size);
+ else
+ va = (u32)uefi_ioremap(paddr, size);
+ if (!va)
+ return 0;
+ region->virt_addr = va;
+
+ if (uefi_debug)
+ pr_info(" %016llx-%016llx => 0x%08x : (%s)\n",
+ paddr, paddr + size - 1, va,
+ md->attribute & EFI_MEMORY_WB ? "WB" : "I/O");
+
+ return 1;
+}
+
+static int __init remap_regions(void)
+{
+ void *p;
+ efi_memory_desc_t *md;
+ int mapped_regions;
+
+ memmap.phys_map = uefi_remap(uefi_boot_mmap, uefi_boot_mmap_size);
+ if (!memmap.phys_map)
+ return 0;
+
+ memmap.map_end = memmap.phys_map + uefi_boot_mmap_size;
+ memmap.desc_size = uefi_mmap_desc_size;
+ memmap.desc_version = uefi_mmap_desc_ver;
+
+ /* Allocate space for the physical region map */
+ memmap.map = kzalloc(memmap.nr_map * memmap.desc_size, GFP_ATOMIC);
+ if (!memmap.map)
+ return 0;
+
+ mapped_regions = 0;
+ for (p = memmap.phys_map; p < memmap.map_end; p += memmap.desc_size) {
+ md = p;
+ if (is_discardable_region(md))
+ continue;
+
+ if (!remap_region(p, mapped_regions++))
+ return 0;
+ }
+
+ memmap.map_end = memmap.map + mapped_regions * memmap.desc_size;
+ efi.memmap = &memmap;
+
+ uefi_unmap(memmap.phys_map);
+ memmap.phys_map = efi_lookup_mapped_addr(uefi_boot_mmap);
+ efi.systab = efi_lookup_mapped_addr(uefi_system_table);
+ if (efi.systab)
+ set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+ /*
+ * efi.systab->runtime is a 32-bit pointer to something guaranteed by
+ * the UEFI specification to be 1:1 mapped in a 4GB address space.
+ */
+ runtime = efi_lookup_mapped_addr((u32)efi.systab->runtime);
+
+ return 1;
+}
+
+
+/*
+ * This function switches the UEFI runtime services to virtual mode.
+ * This operation must be performed only once in the system's lifetime,
+ * including any kecec calls.
+ *
+ * This must be done with a 1:1 mapping. The current implementation
+ * resolves this by disabling the MMU.
+ */
+efi_status_t __init phys_set_virtual_address_map(u32 memory_map_size,
+ u32 descriptor_size,
+ u32 descriptor_version,
+ efi_memory_desc_t *dsc)
+{
+ uefi_phys_call_t *phys_set_map;
+ efi_status_t status;
+
+ phys_call_prologue();
+
+ phys_set_map = (void *)(unsigned long)virt_to_phys(uefi_phys_call);
+
+ /* Called with caches disabled, returns with caches enabled */
+ status = phys_set_map(efi.set_virtual_address_map,
+ PAGE_OFFSET - PHYS_OFFSET,
+ memory_map_size, descriptor_size,
+ descriptor_version, dsc);
+
+ phys_call_epilogue();
+
+ return status;
+}
+
+/*
+ * Called explicitly from init/mm.c
+ */
+void __init efi_enter_virtual_mode(void)
+{
+ efi_status_t status;
+ u32 mmap_phys_addr;
+
+ if (!efi_enabled(EFI_BOOT)) {
+ pr_info("UEFI services will not be available.\n");
+ return;
+ }
+
+ pr_info("Remapping and enabling UEFI services.\n");
+
+ /* Map the regions we memblock_remove:d earlier into kernel
+ address space */
+ if (!remap_regions()) {
+ pr_info("Failed to remap UEFI regions - runtime services will not be available.\n");
+ return;
+ }
+
+ /* Call SetVirtualAddressMap with the physical address of the map */
+ efi.set_virtual_address_map = runtime->set_virtual_address_map;
+
+ /*
+ * __virt_to_phys() takes an unsigned long and returns a phys_addr_t
+ * memmap.phys_map is a void *
+ * The gymnastics below makes this compile validly with/without LPAE.
+ */
+ mmap_phys_addr = __virt_to_phys((u32)memmap.map);
+ memmap.phys_map = (void *)mmap_phys_addr;
+
+ status = phys_set_virtual_address_map(memmap.nr_map * memmap.desc_size,
+ memmap.desc_size,
+ memmap.desc_version,
+ memmap.phys_map);
+ if (status != EFI_SUCCESS) {
+ pr_info("Failed to set UEFI virtual address map!\n");
+ return;
+ }
+
+ /* Set up function pointers for efivars */
+ efi.get_variable = runtime->get_variable;
+ efi.get_next_variable = runtime->get_next_variable;
+ efi.set_variable = runtime->set_variable;
+ efi.set_virtual_address_map = NULL;
+
+ set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+}
diff --git a/arch/arm/kernel/uefi_phys.S b/arch/arm/kernel/uefi_phys.S
new file mode 100644
index 0000000..a999626
--- /dev/null
+++ b/arch/arm/kernel/uefi_phys.S
@@ -0,0 +1,69 @@
+/*
+ * arch/arm/kernel/uefi_phys.S
+ *
+ * Copyright (C) 2013 Linaro Ltd.
+ *
+ * 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 <asm/assembler.h>
+#include <asm/cp15.h>
+#include <linux/linkage.h>
+#define PAR_MASK 0xfff
+
+ .text
+@ uefi_phys_call(*f, virt_phys_offset, a, b, c, d, ...)
+ .align 5
+ .pushsection .idmap.text, "ax"
+ENTRY(uefi_phys_call)
+ @ Save physical context
+ mov r12, sp
+ ldr sp, =tmpstack
+ stmfd sp, {r4-r5, r12, lr} @ push is redefined by asm/assembler.h
+
+ mov r4, r1
+
+ @ Extract function pointer (don't write lr again before call)
+ mov lr, r0
+
+ @ Shift arguments down
+ mov r0, r2
+ mov r1, r3
+ ldr r2, [r12], #4
+ ldr r3, [r12], #4
+
+ @ Convert sp to physical
+ sub r12, r12, r4
+ mov sp, r12
+
+ @ Disable MMU
+ ldr r5, =(CR_M)
+ update_sctlr r12, , r5
+ isb
+
+ @ Make call
+ blx lr
+
+ @ Enable MMU + Caches
+ ldr r4, =(CR_I | CR_C | CR_M)
+ update_sctlr r12, r4
+ isb
+
+ ldr sp, =tmpstack_top
+ ldmfd sp, {r4-r5, r12, lr}
+
+ @ Restore virtual sp and return
+ mov sp, r12
+ bx lr
+
+ .align 3
+tmpstack_top:
+ .long 0 @ r4
+ .long 0 @ r5
+ .long 0 @ r12
+ .long 0 @ lr
+tmpstack:
+ENDPROC(uefi_phys_call)
+ .popsection
--
1.7.10.4

2014-02-05 17:09:41

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 12/22] Documentation: arm: add UEFI support documentation

This patch provides documentation of the [U]EFI runtime service and
configuration features for the arm architecture.

Changes since v1/v2:
- Complete rewrite.
- New FDT bindings.

Cc: Rob Landley <[email protected]>
Cc: [email protected]

Signed-off-by: Leif Lindholm <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
---
Documentation/arm/00-INDEX | 3 +++
Documentation/arm/uefi.txt | 64 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 Documentation/arm/uefi.txt

diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX
index 36420e1..b3af704 100644
--- a/Documentation/arm/00-INDEX
+++ b/Documentation/arm/00-INDEX
@@ -34,3 +34,6 @@ nwfpe/
- NWFPE floating point emulator documentation
swp_emulation
- SWP/SWPB emulation handler/logging description
+
+uefi.txt
+ - [U]EFI configuration and runtime services documentation
diff --git a/Documentation/arm/uefi.txt b/Documentation/arm/uefi.txt
new file mode 100644
index 0000000..d60030a
--- /dev/null
+++ b/Documentation/arm/uefi.txt
@@ -0,0 +1,64 @@
+UEFI, the Unified Extensible Firmware Interface, is a specification
+governing the behaviours of compatible firmware interfaces. It is
+maintained by the UEFI Forum - http://www.uefi.org/.
+
+UEFI is an evolution of its predecessor 'EFI', so the terms EFI and
+UEFI are used somewhat interchangeably in this document and associated
+source code. As a rule, anything new uses 'UEFI', whereas 'EFI' refers
+to legacy code or specifications.
+
+UEFI support in Linux
+=====================
+Booting on a platform with firmware compliant with the UEFI specification
+makes it possible for the kernel to support additional features:
+- UEFI Runtime Services
+- Retrieving various configuration information through the standardised
+ interface of UEFI configuration tables. (ACPI, SMBIOS, ...)
+
+For actually enabling [U]EFI support, enable:
+- CONFIG_EFI=y
+- CONFIG_EFI_VARS=y or m
+
+The implementation depends on receiving information about the UEFI environment
+in a Flattened Device Tree (FDT) - so is only available with CONFIG_OF.
+
+UEFI stub
+=========
+The "stub" is a feature that extends the Image/zImage into a valid UEFI
+PE/COFF executable, including a loader application that makes it possible to
+load the kernel directly from the UEFI shell, boot menu, or one of the
+lightweight bootloaders like Gummiboot or rEFInd.
+
+The kernel image built with stub support remains a valid kernel image for
+booting in non-UEFI environments.
+
+UEFI kernel support on ARM
+==========================
+UEFI kernel support on the ARM architectures (arm and arm64) is only available
+when boot is performed through the stub.
+
+When booting in UEFI mode, the stub deletes any memory nodes from a provided DT.
+Instead, the kernel reads the UEFI memory map.
+
+The stub populates the FDT /chosen node with (and the kernel scans for) the
+following parameters:
+________________________________________________________________________________
+Name | Size | Description
+================================================================================
+linux,uefi-system-table | 64-bit | Physical address of the UEFI System Table.
+--------------------------------------------------------------------------------
+linux,uefi-mmap-start | 64-bit | Physical address of the UEFI memory map,
+ | | populated by the UEFI GetMemoryMap() call.
+--------------------------------------------------------------------------------
+linux,uefi-mmap-size | 32-bit | Size in bytes of the UEFI memory map
+ | | pointed to in previous entry.
+--------------------------------------------------------------------------------
+linux,uefi-mmap-desc-size | 32-bit | Size in bytes of each entry in the UEFI
+ | | memory map.
+--------------------------------------------------------------------------------
+linux,uefi-mmap-desc-ver | 32-bit | Version of the mmap descriptor format.
+--------------------------------------------------------------------------------
+linux,uefi-stub-kern-ver | string | Copy of linux_banner from build.
+--------------------------------------------------------------------------------
+
+For verbose debug messages, specify 'uefi_debug' on the kernel command line.
--
1.7.10.4

2014-02-05 17:10:12

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 09/22] Add shared arm/arm64 EFI stub

From: Roy Franz <[email protected]>

This patch adds the EFI stub entry point that is shared
by the arm/arm64 architectures. Each arch will implement
the handle_kernel_image() function that handles the arch
specific load address and boot protocol requirements.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
drivers/firmware/efi/arm-stub.c | 145 +++++++++++++++++++++++++++++++++++++++
drivers/firmware/efi/fdt.c | 31 +++++++--
2 files changed, 171 insertions(+), 5 deletions(-)
create mode 100644 drivers/firmware/efi/arm-stub.c

diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c
new file mode 100644
index 0000000..aefe963
--- /dev/null
+++ b/drivers/firmware/efi/arm-stub.c
@@ -0,0 +1,145 @@
+/*
+ * EFI stub implementation that is shared by arm and arm64 architectures.
+ * This should be #included by the EFI stub implementation files.
+ *
+ * Copyright (C) 2013,2014 Linaro Limited
+ * Roy Franz <[email protected]
+ * Copyright (C) 2013 Red Hat, Inc.
+ * Mark Salter <[email protected]>
+ *
+ * This file is part of the Linux kernel, and is made available under the
+ * terms of the GNU General Public License version 2.
+ *
+ */
+
+/*
+ * This function handles the architcture specific differences between arm and
+ * arm64 regarding where the kernel image must be loaded and any memory that
+ * must be reserved. On failure it is required to free all
+ * all allocations it has made.
+ */
+static efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+ unsigned long *image_addr,
+ unsigned long *image_size,
+ unsigned long *reserve_addr,
+ unsigned long *reserve_size,
+ unsigned long dram_base,
+ efi_loaded_image_t *image);
+/*
+ * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint
+ * that is described in the PE/COFF header. Most of the code is the same
+ * for both archictectures, with the arch-specific code provided in the
+ * handle_kernel_image() function.
+ */
+unsigned long 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 = 0;
+ unsigned long dram_base;
+ /* addr/point and size pairs for memory management*/
+ unsigned long initrd_addr;
+ u64 initrd_size = 0;
+ unsigned long fdt_addr; /* Original DTB */
+ u64 fdt_size = 0; /* We don't get size from configuration table */
+ char *cmdline_ptr = NULL;
+ int cmdline_size = 0;
+ unsigned long new_fdt_addr;
+ efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
+ unsigned long reserve_addr = 0;
+ unsigned long reserve_size = 0;
+
+ /* Check if we were booted by the EFI firmware */
+ if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+ goto fail;
+
+ pr_efi(sys_table, "Booting Linux Kernel...\n");
+
+ /*
+ * Get a handle to the loaded image protocol. This is used to get
+ * information about the running image, such as size and the command
+ * line.
+ */
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+ handle, &loaded_image_proto, (void *)&image);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to get loaded image protocol\n");
+ goto fail;
+ }
+
+ dram_base = get_dram_base(sys_table);
+ if (dram_base == EFI_ERROR) {
+ pr_efi_err(sys_table, "Failed to find DRAM base\n");
+ goto fail;
+ }
+ status = handle_kernel_image(sys_table, image_addr, &image_size,
+ &reserve_addr,
+ &reserve_size,
+ dram_base, image);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to relocate kernel\n");
+ goto fail;
+ }
+
+ /*
+ * Get the command line from EFI, using the LOADED_IMAGE
+ * protocol. We are going to copy the command line into the
+ * device tree, so this can be allocated anywhere.
+ */
+ cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image,
+ &cmdline_size);
+ if (!cmdline_ptr) {
+ pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
+ goto fail_free_image;
+ }
+
+ /* Load a device tree from the configuration table, if present. */
+ fdt_addr = (uintptr_t)get_fdt(sys_table);
+ if (!fdt_addr) {
+ 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(sys_table, "Failed to load device tree!\n");
+ goto fail_free_cmdline;
+ }
+ }
+
+ status = handle_cmdline_files(sys_table, image, cmdline_ptr,
+ "initrd=", dram_base + SZ_512M,
+ (unsigned long *)&initrd_addr,
+ (unsigned long *)&initrd_size);
+ if (status != EFI_SUCCESS)
+ pr_efi_err(sys_table, "Failed initrd from command line!\n");
+
+ new_fdt_addr = fdt_addr;
+ status = allocate_new_fdt_and_exit_boot(sys_table, handle,
+ &new_fdt_addr, dram_base + MAX_FDT_OFFSET,
+ initrd_addr, initrd_size, cmdline_ptr,
+ fdt_addr, fdt_size);
+
+ /*
+ * If all went well, we need to return the FDT address to the
+ * calling function so it can be passed to kernel as part of
+ * the kernel boot protocol.
+ */
+ if (status == EFI_SUCCESS)
+ return new_fdt_addr;
+
+ pr_efi_err(sys_table, "Failed to update FDT and exit boot services\n");
+
+ efi_free(sys_table, initrd_size, initrd_addr);
+ efi_free(sys_table, fdt_size, fdt_addr);
+
+fail_free_cmdline:
+ efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
+
+fail_free_image:
+ efi_free(sys_table, image_size, *image_addr);
+ efi_free(sys_table, reserve_size, reserve_addr);
+fail:
+ return EFI_ERROR;
+}
diff --git a/drivers/firmware/efi/fdt.c b/drivers/firmware/efi/fdt.c
index a602b0a..4510f97 100644
--- a/drivers/firmware/efi/fdt.c
+++ b/drivers/firmware/efi/fdt.c
@@ -11,6 +11,7 @@
*/

static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
+ unsigned long orig_fdt_size,
void *fdt, int new_fdt_size, char *cmdline_ptr,
u64 initrd_addr, u64 initrd_size,
efi_memory_desc_t *memory_map,
@@ -32,7 +33,27 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";

- status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+ /* Do some checks on provided FDT, if it exists*/
+ if (orig_fdt) {
+ if (fdt_check_header(orig_fdt)) {
+ pr_efi_err(sys_table, "Device Tree header not valid!\n");
+ return EFI_LOAD_ERROR;
+ }
+ /*
+ * We don't get the size of the FDT if we get if from a
+ * configuration table.
+ */
+ if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
+ pr_efi_err(sys_table, "Truncated device tree! foo!\n");
+ return EFI_LOAD_ERROR;
+ }
+ }
+
+ if (orig_fdt)
+ status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
+ else
+ status = fdt_create_empty_tree(fdt, new_fdt_size);
+
if (status != 0)
goto fdt_set_fail;

@@ -180,10 +201,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
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);
+ (void *)fdt_addr, fdt_size,
+ (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)
--
1.7.10.4

2014-02-05 17:05:19

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 04/22] efi-stub.txt updates for ARM

From: Roy Franz <[email protected]>

Update efi-stub.txt documentation to be more general
and not x86 specific. Add ARM only "dtb=" command
line option description.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Acked-by: Grant Likely <[email protected]>
---
Documentation/efi-stub.txt | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt
index c628788..26be7b0 100644
--- a/Documentation/efi-stub.txt
+++ b/Documentation/efi-stub.txt
@@ -1,13 +1,16 @@
The EFI Boot Stub
---------------------------

-On the x86 platform, a bzImage can masquerade as a PE/COFF image,
-thereby convincing EFI firmware loaders to load it as an EFI
-executable. The code that modifies the bzImage header, along with the
-EFI-specific entry point that the firmware loader jumps to are
-collectively known as the "EFI boot stub", and live in
+On the x86 and ARM platforms, a kernel zImage/bzImage can masquerade
+as a PE/COFF image, thereby convincing EFI firmware loaders to load
+it as an EFI executable. The code that modifies the bzImage header,
+along with the EFI-specific entry point that the firmware loader
+jumps to are collectively known as the "EFI boot stub", and live in
arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c,
-respectively.
+respectively. For ARM the EFI stub is implemented in
+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.

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
@@ -23,7 +26,9 @@ The bzImage located in arch/x86/boot/bzImage must be copied to the EFI
System Partition (ESP) and renamed with the extension ".efi". Without
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.
+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.


**** Passing kernel parameters from the EFI shell
@@ -63,3 +68,11 @@ Notice how bzImage.efi can be specified with a relative path. That's
because the image we're executing is interpreted by the EFI shell,
which understands relative paths, whereas the rest of the command line
is passed to bzImage.efi.
+
+
+**** The "dtb=" option
+
+For the ARM 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 processed in the same manner as the "initrd=" option that is
+described above.
--
1.7.10.4

2014-02-05 17:10:57

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 08/22] Add strstr to compressed string.c for ARM.

From: Roy Franz <[email protected]>

The shared efi-stub-helper.c functions require a strstr implementation.
The EFI stub is part of the decompressor, so it does not use the kernel
strstr() implementation. This patch adds a strstr() implementation to
the string.c file for the decompressor, with the implementation copied
from the arch/x86/boot/string.c file used by the x86 decompressor.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
Reviewed-by: Grant Likely <[email protected]>
---
arch/arm/boot/compressed/string.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/arch/arm/boot/compressed/string.c b/arch/arm/boot/compressed/string.c
index 36e53ef..5397792 100644
--- a/arch/arm/boot/compressed/string.c
+++ b/arch/arm/boot/compressed/string.c
@@ -111,6 +111,27 @@ char *strchr(const char *s, int c)
return (char *)s;
}

+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char *strstr(const char *s1, const char *s2)
+{
+ size_t l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *)s1;
+ l1 = strlen(s1);
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1, s2, l2))
+ return (char *)s1;
+ s1++;
+ }
+ return NULL;
+}
#undef memset

void *memset(void *s, int c, size_t count)
--
1.7.10.4

2014-02-05 17:11:31

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 06/22] Add helper functions used by arm/arm64

From: Roy Franz <[email protected]>

Add the get_dram_base() function and efi_call_physN() macros
that are shared by arm/arm64.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
drivers/firmware/efi/efi-stub-helper.c | 63 +++++++++++++++++++++++++-------
1 file changed, 50 insertions(+), 13 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index eb5d2eb..8477a72 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -11,6 +11,27 @@
*/
#define EFI_READ_CHUNK_SIZE (1024 * 1024)

+/* error code which can't be mistaken for valid address */
+#define EFI_ERROR (~0UL)
+
+# if !defined(CONFIG_X86)
+/*
+ * EFI function call wrappers. These are not required for arm/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)
+#endif
+
struct file_info {
efi_file_handle_t *handle;
u64 size;
@@ -92,6 +113,32 @@ fail:
return status;
}

+
+static unsigned long __init get_dram_base(efi_system_table_t *sys_table)
+{
+ efi_status_t status;
+ unsigned long map_size;
+ unsigned long membase = EFI_ERROR;
+ struct efi_memory_map map;
+ efi_memory_desc_t *md;
+
+ status = efi_get_memory_map(sys_table, (efi_memory_desc_t **)&map.map,
+ &map_size, &map.desc_size, NULL, NULL);
+ if (status != EFI_SUCCESS)
+ return membase;
+
+ map.map_end = map.map + map_size;
+
+ for_each_efi_memory_desc(&map, md)
+ if (md->attribute & EFI_MEMORY_WB)
+ if (membase > md->phys_addr)
+ membase = md->phys_addr;
+
+ efi_call_phys1(sys_table->boottime->free_pool, map.map);
+
+ return membase;
+}
+
/*
* Allocate at the highest possible address that is not above 'max'.
*/
@@ -610,19 +657,9 @@ static char *efi_convert_cmdline_to_ascii(efi_system_table_t *sys_table_arg,
options = &zero;
}

- options_size++; /* NUL termination */
-#ifdef CONFIG_ARM
- /*
- * For ARM, allocate at a high address to avoid reserved
- * regions at low addresses that we don't know the specfics of
- * at the time we are processing the command line.
- */
- status = efi_high_alloc(sys_table_arg, options_size, 0,
- &cmdline_addr, 0xfffff000);
-#else
- status = efi_low_alloc(sys_table_arg, options_size, 0,
- &cmdline_addr);
-#endif
+ options_size++; /* NULL termination */
+
+ status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr);
if (status != EFI_SUCCESS)
return NULL;

--
1.7.10.4

2014-02-05 17:05:15

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 01/22] arm: break part of __soft_restart out into separate function

Certain operations can be considered mandatory for any piece of code
preparing to switch off the MMU. Break this out into separate function
idmap_prepare().

Signed-off-by: Leif Lindholm <[email protected]>
Suggested-by: Will Deacon <[email protected]>
Acked-by: Will Deacon <[email protected]>
---
arch/arm/include/asm/idmap.h | 1 +
arch/arm/kernel/process.c | 12 +-----------
arch/arm/mm/idmap.c | 15 +++++++++++++++
3 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/arch/arm/include/asm/idmap.h b/arch/arm/include/asm/idmap.h
index bf863ed..2e914a8 100644
--- a/arch/arm/include/asm/idmap.h
+++ b/arch/arm/include/asm/idmap.h
@@ -10,5 +10,6 @@
extern pgd_t *idmap_pgd;

void setup_mm_for_reboot(void);
+void idmap_prepare(void);

#endif /* __ASM_IDMAP_H */
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 92f7b15..91b4cec 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -75,17 +75,7 @@ static void __soft_restart(void *addr)
{
phys_reset_t phys_reset;

- /* Take out a flat memory mapping. */
- setup_mm_for_reboot();
-
- /* Clean and invalidate caches */
- flush_cache_all();
-
- /* Turn off caching */
- cpu_proc_fin();
-
- /* Push out any further dirty data, and ensure cache is empty */
- flush_cache_all();
+ idmap_prepare();

/* Switch to the identity mapping. */
phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
diff --git a/arch/arm/mm/idmap.c b/arch/arm/mm/idmap.c
index 8e0e52e..5c85779 100644
--- a/arch/arm/mm/idmap.c
+++ b/arch/arm/mm/idmap.c
@@ -122,3 +122,18 @@ void setup_mm_for_reboot(void)
local_flush_tlb_all();
#endif
}
+
+void idmap_prepare(void)
+{
+ /* Take out a flat memory mapping. */
+ setup_mm_for_reboot();
+
+ /* Clean and invalidate caches */
+ flush_cache_all();
+
+ /* Turn off caching */
+ cpu_proc_fin();
+
+ /* Push out any further dirty data, and ensure cache is empty */
+ flush_cache_all();
+}
--
1.7.10.4

2014-02-05 17:11:50

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 05/22] Add shared printk wrapper for consistent prefixing

From: Roy Franz <[email protected]>

Add a wrapper for printk to standardize the prefix for informational and
error messages from the EFI stub.

Signed-off-by: Roy Franz <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
drivers/firmware/efi/efi-stub-helper.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
index b6bffbf..eb5d2eb 100644
--- a/drivers/firmware/efi/efi-stub-helper.c
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -45,6 +45,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
}
}

+#define pr_efi(sys_table, msg) efi_printk(sys_table, "EFI stub: "msg)
+#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
+

static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg,
efi_memory_desc_t **map,
@@ -324,7 +327,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
nr_files * sizeof(*files),
(void **)&files);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n");
+ pr_efi_err(sys_table_arg, "Failed to alloc mem for file handle list\n");
goto fail;
}

@@ -376,13 +379,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
image->device_handle, &fs_proto,
(void **)&io);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
+ pr_efi_err(sys_table_arg, "Failed to handle fs_proto\n");
goto free_files;
}

status = efi_call_phys2(io->open_volume, io, &fh);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to open volume\n");
+ pr_efi_err(sys_table_arg, "Failed to open volume\n");
goto free_files;
}
}
@@ -390,7 +393,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
status = efi_call_phys5(fh->open, fh, &h, filename_16,
EFI_FILE_MODE_READ, (u64)0);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to open file: ");
+ pr_efi_err(sys_table_arg, "Failed to open file: ");
efi_char16_printk(sys_table_arg, filename_16);
efi_printk(sys_table_arg, "\n");
goto close_handles;
@@ -402,7 +405,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
status = efi_call_phys4(h->get_info, h, &info_guid,
&info_sz, NULL);
if (status != EFI_BUFFER_TOO_SMALL) {
- efi_printk(sys_table_arg, "Failed to get file info size\n");
+ pr_efi_err(sys_table_arg, "Failed to get file info size\n");
goto close_handles;
}

@@ -411,7 +414,7 @@ grow:
EFI_LOADER_DATA, info_sz,
(void **)&info);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
+ pr_efi_err(sys_table_arg, "Failed to alloc mem for file info\n");
goto close_handles;
}

@@ -427,7 +430,7 @@ grow:
efi_call_phys1(sys_table_arg->boottime->free_pool, info);

if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to get file info\n");
+ pr_efi_err(sys_table_arg, "Failed to get file info\n");
goto close_handles;
}

@@ -446,13 +449,13 @@ grow:
status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000,
&file_addr, max_addr);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to alloc highmem for files\n");
+ pr_efi_err(sys_table_arg, "Failed to alloc highmem for files\n");
goto close_handles;
}

/* We've run out of free low memory. */
if (file_addr > max_addr) {
- efi_printk(sys_table_arg, "We've run out of free low memory\n");
+ pr_efi_err(sys_table_arg, "We've run out of free low memory\n");
status = EFI_INVALID_PARAMETER;
goto free_file_total;
}
@@ -473,7 +476,7 @@ grow:
&chunksize,
(void *)addr);
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "Failed to read file\n");
+ pr_efi_err(sys_table_arg, "Failed to read file\n");
goto free_file_total;
}
addr += chunksize;
@@ -558,7 +561,7 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
&new_addr);
}
if (status != EFI_SUCCESS) {
- efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n");
+ pr_efi_err(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n");
return status;
}

--
1.7.10.4

2014-02-05 17:12:13

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 03/22] efi: add helper function to get UEFI params from FDT

From: Mark Salter <[email protected]>

ARM and ARM64 architectures use the device tree to pass UEFI parameters
from stub to kernel. These parameters are things known to the stub but
not discoverable by the kernel after the stub calls ExitBootSerives().
There is a helper function in:

drivers/firmware/efi/fdt.c

which the stub uses to add the UEFI parameters to the device tree.
This patch adds a complimentary helper function which UEFI runtime
support may use to retrieve the parameters from the device tree.
If an architecture wants to use this helper, it should select
CONFIG_UEFI_PARAMS_FROM_FDT.

Signed-off-by: Mark Salter <[email protected]>
Signed-off-by: Leif Lindholm <[email protected]>
---
drivers/firmware/efi/Kconfig | 7 ++++
drivers/firmware/efi/efi.c | 79 ++++++++++++++++++++++++++++++++++++++++++
include/linux/efi.h | 9 +++++
3 files changed, 95 insertions(+)

diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 1e75f48..d3fe28d 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -47,6 +47,13 @@ config EFI_RUNTIME_MAP

See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map.

+config UEFI_PARAMS_FROM_FDT
+ bool
+ help
+ Select this config option from the architecture Kconfig if
+ the EFI runtime support gets system table address, memory
+ map address, and other parameters from the device tree.
+
endmenu

config UEFI_CPER
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index b25b36b..7f6e977 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -20,6 +20,8 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/efi.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
#include <linux/io.h>

struct efi __read_mostly efi = {
@@ -318,3 +320,80 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables)

return 0;
}
+
+#ifdef CONFIG_UEFI_PARAMS_FROM_FDT
+
+#define UEFI_PARAM(name, prop, field) \
+ { \
+ { name }, \
+ { prop }, \
+ offsetof(struct efi_fdt_params, field), \
+ FIELD_SIZEOF(struct efi_fdt_params, field) \
+ }
+
+static __initdata struct {
+ const char name[32];
+ const char propname[32];
+ int offset;
+ int size;
+} dt_params[] = {
+ UEFI_PARAM("System Table", "linux,uefi-system-table", system_table),
+ UEFI_PARAM("MemMap Address", "linux,uefi-mmap-start", mmap),
+ UEFI_PARAM("MemMap Size", "linux,uefi-mmap-size", mmap_size),
+ UEFI_PARAM("MemMap Desc. Size", "linux,uefi-mmap-desc-size", desc_size),
+ UEFI_PARAM("MemMap Desc. Version", "linux,uefi-mmap-desc-ver", desc_ver)
+};
+
+struct param_info {
+ int verbose;
+ void *params;
+};
+
+static int __init fdt_find_uefi_params(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ struct param_info *info = data;
+ void *prop, *dest;
+ unsigned long len;
+ u64 val;
+ int i;
+
+ if (depth != 1 ||
+ (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
+ return 0;
+
+ pr_info("Getting parameters from FDT:\n");
+
+ for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
+ prop = of_get_flat_dt_prop(node, dt_params[i].propname, &len);
+ if (!prop) {
+ pr_err("Can't find %s in device tree!\n",
+ dt_params[i].name);
+ return 0;
+ }
+ dest = info->params + dt_params[i].offset;
+
+ val = of_read_number(prop, len / sizeof(u32));
+
+ if (dt_params[i].size == sizeof(u32))
+ *(u32 *)dest = val;
+ else
+ *(u64 *)dest = val;
+
+ if (info->verbose)
+ pr_info(" %s: 0x%0*llx\n", dt_params[i].name,
+ dt_params[i].size * 2, val);
+ }
+ return 1;
+}
+
+int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose)
+{
+ struct param_info info;
+
+ info.verbose = verbose;
+ info.params = params;
+
+ return of_scan_flat_dt(fdt_find_uefi_params, &info);
+}
+#endif /* CONFIG_UEFI_PARAMS_FROM_FDT */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index a3276da..d450673 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -483,6 +483,14 @@ struct efi_memory_map {
unsigned long desc_size;
};

+struct efi_fdt_params {
+ u64 system_table;
+ u64 mmap;
+ u32 mmap_size;
+ u32 desc_size;
+ u32 desc_ver;
+};
+
typedef struct {
u32 revision;
void *parent_handle;
@@ -620,6 +628,7 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource,
extern void efi_get_time(struct timespec *now);
extern int efi_set_rtc_mmss(const struct timespec *now);
extern void efi_reserve_boot_services(void);
+extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
extern struct efi_memory_map memmap;

/* Iterate through an efi_memory_map */
--
1.7.10.4

2014-02-05 17:12:51

by Leif Lindholm

[permalink] [raw]
Subject: [PATCH 02/22] arm: add new asm macro update_sctlr

A new macro for setting/clearing bits in the SCTLR.

Signed-off-by: Leif Lindholm <[email protected]>
Suggested-by: Will Deacon <[email protected]>
Cc: Will Deacon <[email protected]>
---
arch/arm/include/asm/assembler.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 5c22851..e8ca24b 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -383,4 +383,18 @@ THUMB( orr \reg , \reg , #PSR_T_BIT )
#endif
.endm

+#ifdef CONFIG_CPU_CP15
+/* Macro for setting/clearing bits in sctlr */
+ .macro update_sctlr, tmp:req, set=, clear=
+ mrc p15, 0, \tmp, c1, c0, 0
+ .ifnc \set,
+ orr \tmp, \set
+ .endif
+ .ifnc \clear,
+ bic \tmp, \tmp, \clear
+ .endif
+ mcr p15, 0, \tmp, c1, c0, 0
+ .endm
+#endif
+
#endif /* __ASM_ASSEMBLER_H__ */
--
1.7.10.4

2014-02-05 17:34:19

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH 05/22] Add shared printk wrapper for consistent prefixing

On Wed, 2014-02-05 at 17:03 +0000, Leif Lindholm wrote:
> From: Roy Franz <[email protected]>
>
> Add a wrapper for printk to standardize the prefix for informational and
> error messages from the EFI stub.

trivia:

> diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
[]
> @@ -45,6 +45,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str)
> }
> }
>
> +#define pr_efi(sys_table, msg) efi_printk(sys_table, "EFI stub: "msg)
> +#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)

Perhaps it'd be better to use a space after the second "

Also, maybe pr_efi should be pr_efi_info.
That would quiet any checkpatch noise around long msg uses.


2014-02-05 18:01:47

by Will Deacon

[permalink] [raw]
Subject: Re: [PATCH 02/22] arm: add new asm macro update_sctlr

On Wed, Feb 05, 2014 at 05:03:53PM +0000, Leif Lindholm wrote:
> A new macro for setting/clearing bits in the SCTLR.
>
> Signed-off-by: Leif Lindholm <[email protected]>
> Suggested-by: Will Deacon <[email protected]>
> Cc: Will Deacon <[email protected]>
> ---
> arch/arm/include/asm/assembler.h | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)

Acked-by: Will Deacon <[email protected]>

(although really minor comment below)

> diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
> index 5c22851..e8ca24b 100644
> --- a/arch/arm/include/asm/assembler.h
> +++ b/arch/arm/include/asm/assembler.h
> @@ -383,4 +383,18 @@ THUMB( orr \reg , \reg , #PSR_T_BIT )
> #endif
> .endm
>
> +#ifdef CONFIG_CPU_CP15
> +/* Macro for setting/clearing bits in sctlr */
> + .macro update_sctlr, tmp:req, set=, clear=
> + mrc p15, 0, \tmp, c1, c0, 0
> + .ifnc \set,
> + orr \tmp, \set

I'd prefer the 3-arg form here for consistency (with this macro and the
rest of the file).

Will

2014-02-05 18:09:16

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: [PATCH 21/22] arm: efistub: ignore dtb= when UEFI SecureBoot is enabled

On 5 February 2014 18:04, Leif Lindholm <[email protected]> wrote:
> From: Ard Biesheuvel <[email protected]>
>
> Loading unauthenticated FDT blobs directly from storage is a security hazard,
> so this should only be allowed when running with UEFI Secure Boot disabled.
>
> Signed-off-by: Ard Biesheuvel <[email protected]>
> Signed-off-by: Leif Lindholm <[email protected]>
> ---
> drivers/firmware/efi/arm-stub.c | 4 +++-
> drivers/firmware/efi/efi-stub-helper.c | 24 ++++++++++++++++++++++++
> 2 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/firmware/efi/arm-stub.c b/drivers/firmware/efi/arm-stub.c
> index b505fde..c651082 100644
> --- a/drivers/firmware/efi/arm-stub.c
> +++ b/drivers/firmware/efi/arm-stub.c
> @@ -95,7 +95,9 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
>
> /* Load a device tree from the configuration table, if present. */
> fdt_addr = (uintptr_t)get_fdt(sys_table);
> - if (!fdt_addr) {
> + if (efi_secureboot_enabled(sys_table))
> + pr_efi(sys_table, "UEFI Secure Boot is enabled, ignoring dtb= commandline option.\n");

I am pretty sure my original patch had braces on both branches of the if () :-)

Also, I think the precedence is backward here: dtb= should trump
config table, not the other way around.

--
Ard.


> + else if (!fdt_addr) {
> status = handle_cmdline_files(sys_table, image, cmdline_ptr,
> "dtb=",
> ~0UL, (unsigned long *)&fdt_addr,
> diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
> index 2ee69ea..6221be7 100644
> --- a/drivers/firmware/efi/efi-stub-helper.c
> +++ b/drivers/firmware/efi/efi-stub-helper.c
> @@ -721,3 +721,27 @@ static char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
> *cmd_line_len = options_bytes;
> return (char *)cmdline_addr;
> }
> +
> +static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
> +{
> + static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID;
> + static efi_char16_t const var_name[] __initconst = {
> + 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 };
> +
> + efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable;
> + unsigned long size = sizeof(u8);
> + efi_status_t status;
> + u8 val;
> +
> + status = efi_call_phys5(f_getvar, (efi_char16_t *)var_name,
> + (efi_guid_t *)&var_guid, NULL, &size, &val);
> +
> + switch (status) {
> + case EFI_SUCCESS:
> + return val;
> + case EFI_NOT_FOUND:
> + return 0;
> + default:
> + return 1;
> + }
> +}
> --
> 1.7.10.4
>

2014-02-13 11:17:15

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 03/22] efi: add helper function to get UEFI params from FDT

On Wed, 05 Feb, at 05:03:54PM, Leif Lindholm wrote:
> From: Mark Salter <[email protected]>
>
> ARM and ARM64 architectures use the device tree to pass UEFI parameters
> from stub to kernel. These parameters are things known to the stub but
> not discoverable by the kernel after the stub calls ExitBootSerives().
> There is a helper function in:
>
> drivers/firmware/efi/fdt.c
>
> which the stub uses to add the UEFI parameters to the device tree.
> This patch adds a complimentary helper function which UEFI runtime
> support may use to retrieve the parameters from the device tree.
> If an architecture wants to use this helper, it should select
> CONFIG_UEFI_PARAMS_FROM_FDT.
>
> Signed-off-by: Mark Salter <[email protected]>
> Signed-off-by: Leif Lindholm <[email protected]>
> ---
> drivers/firmware/efi/Kconfig | 7 ++++
> drivers/firmware/efi/efi.c | 79 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/efi.h | 9 +++++
> 3 files changed, 95 insertions(+)

Is there a particular reason this change is being added to
drivers/firmware/efi/efi.c and not drivers/firmware/efi/fdt.c?

--
Matt Fleming, Intel Open Source Technology Center

2014-02-13 11:26:11

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH 06/22] Add helper functions used by arm/arm64

On Wed, 05 Feb, at 05:03:57PM, Leif Lindholm wrote:
> From: Roy Franz <[email protected]>
>
> Add the get_dram_base() function and efi_call_physN() macros
> that are shared by arm/arm64.
>
> Signed-off-by: Roy Franz <[email protected]>
> Signed-off-by: Leif Lindholm <[email protected]>
> ---
> drivers/firmware/efi/efi-stub-helper.c | 63 +++++++++++++++++++++++++-------
> 1 file changed, 50 insertions(+), 13 deletions(-)

These changes should be in drivers/firmware/efi/arm-stub.c -
efi-stub-helper.c is not a dumping ground for random
architecture-specific code.

--
Matt Fleming, Intel Open Source Technology Center

2014-02-13 14:56:47

by Mark Salter

[permalink] [raw]
Subject: Re: [PATCH 03/22] efi: add helper function to get UEFI params from FDT

On Thu, 2014-02-13 at 11:17 +0000, Matt Fleming wrote:
> On Wed, 05 Feb, at 05:03:54PM, Leif Lindholm wrote:
> > From: Mark Salter <[email protected]>
> >
> > ARM and ARM64 architectures use the device tree to pass UEFI parameters
> > from stub to kernel. These parameters are things known to the stub but
> > not discoverable by the kernel after the stub calls ExitBootSerives().
> > There is a helper function in:
> >
> > drivers/firmware/efi/fdt.c
> >
> > which the stub uses to add the UEFI parameters to the device tree.
> > This patch adds a complimentary helper function which UEFI runtime
> > support may use to retrieve the parameters from the device tree.
> > If an architecture wants to use this helper, it should select
> > CONFIG_UEFI_PARAMS_FROM_FDT.
> >
> > Signed-off-by: Mark Salter <[email protected]>
> > Signed-off-by: Leif Lindholm <[email protected]>
> > ---
> > drivers/firmware/efi/Kconfig | 7 ++++
> > drivers/firmware/efi/efi.c | 79 ++++++++++++++++++++++++++++++++++++++++++
> > include/linux/efi.h | 9 +++++
> > 3 files changed, 95 insertions(+)
>
> Is there a particular reason this change is being added to
> drivers/firmware/efi/efi.c and not drivers/firmware/efi/fdt.c?
>

drivers/firmware/efi/fdt.c is included by the stub which may or may not
be linked into kernel image. This helper is used by runtime services and
needs to be built into kernel.

--Mark

2014-02-14 17:43:05

by Catalin Marinas

[permalink] [raw]
Subject: Re: [PATCH 16/22] arm64: Add function to create identity mappings

On Wed, Feb 05, 2014 at 05:04:07PM +0000, Leif Lindholm wrote:
> +void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
> +{
> + pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
> +
> + if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
> + pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
> + return;
> + }
> + __create_mapping(pgd, addr, addr, size, map_io);
> +}

I'm a bit worried because I gave some feedback in the past but it didn't
make to this patch.

What I said on 22nd of January:

The condition above is always false since pgd_index() already ands the
index with (PTRS_PER_PGD - 1). Better check addr against something like
(PTRS_PER_PGD * PGDIR_SIZE) (for clarity, you could do other shifts,
doesn't really matter).

--
Catalin

2014-02-14 19:02:51

by Roy Franz

[permalink] [raw]
Subject: Re: [PATCH 06/22] Add helper functions used by arm/arm64

On Thu, Feb 13, 2014 at 3:26 AM, Matt Fleming <[email protected]> wrote:
> On Wed, 05 Feb, at 05:03:57PM, Leif Lindholm wrote:
>> From: Roy Franz <[email protected]>
>>
>> Add the get_dram_base() function and efi_call_physN() macros
>> that are shared by arm/arm64.
>>
>> Signed-off-by: Roy Franz <[email protected]>
>> Signed-off-by: Leif Lindholm <[email protected]>
>> ---
>> drivers/firmware/efi/efi-stub-helper.c | 63 +++++++++++++++++++++++++-------
>> 1 file changed, 50 insertions(+), 13 deletions(-)
>
> These changes should be in drivers/firmware/efi/arm-stub.c -
> efi-stub-helper.c is not a dumping ground for random
> architecture-specific code.

Hi Matt,

I will move those functions/macros to the arm-stub.c file. I agree
that the macros should go there, as they are the arm specific versions
and conflict with the x86 versions defined elsewhere. These were
there before the arm-stub.c file was added and didn't get moved when
it was. The get_dram_base() function is only used by arm/arm64, but
there is nothing architecture specific about it, which is why I put it
here to begin with. I don't feel strongly about this either way.

Thanks,
Roy


>
> --
> Matt Fleming, Intel Open Source Technology Center

2014-02-17 13:29:49

by Mark Salter

[permalink] [raw]
Subject: Re: [PATCH 16/22] arm64: Add function to create identity mappings

On Fri, 2014-02-14 at 17:42 +0000, Catalin Marinas wrote:
> On Wed, Feb 05, 2014 at 05:04:07PM +0000, Leif Lindholm wrote:
> > +void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io)
> > +{
> > + pgd_t *pgd = &idmap_pg_dir[pgd_index(addr)];
> > +
> > + if (pgd >= &idmap_pg_dir[ARRAY_SIZE(idmap_pg_dir)]) {
> > + pr_warn("BUG: not creating id mapping for 0x%016llx\n", addr);
> > + return;
> > + }
> > + __create_mapping(pgd, addr, addr, size, map_io);
> > +}
>
> I'm a bit worried because I gave some feedback in the past but it didn't
> make to this patch.
>
> What I said on 22nd of January:
>
> The condition above is always false since pgd_index() already ands the
> index with (PTRS_PER_PGD - 1). Better check addr against something like
> (PTRS_PER_PGD * PGDIR_SIZE) (for clarity, you could do other shifts,
> doesn't really matter).
>
Sorry about that. I remember making this change but I don't know what
happened to it.