Message ID | 1372183863-11333-4-git-send-email-leif.lindholm@linaro.org |
---|---|
State | New |
Headers | show |
On Tue, Jun 25, 2013 at 07:11:02PM +0100, Leif Lindholm wrote: > 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 also locates any presented SMBIOS configuration table and stores it > for potential later use by DMI. This appears to duplicate code that's already duplicated between x86 and ia64. We made a mistake there originally - let's not do it again. Having this code in three places is inevitably going to lead to skew and missed bugfixes.
On Tue, Jun 25, 2013 at 7:11 PM, Leif Lindholm <leif.lindholm@linaro.org> wrote: > 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 also locates any presented SMBIOS configuration table and stores it > for potential later use by DMI. > > Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org> > --- > arch/arm/Kconfig | 15 ++ > arch/arm/include/asm/efi.h | 22 +++ > arch/arm/kernel/Makefile | 2 + > arch/arm/kernel/efi.c | 456 ++++++++++++++++++++++++++++++++++++++++++++ > arch/arm/kernel/efi_phys.S | 59 ++++++ > arch/arm/kernel/setup.c | 5 + > 6 files changed, 559 insertions(+) > create mode 100644 arch/arm/include/asm/efi.h > create mode 100644 arch/arm/kernel/efi.c > create mode 100644 arch/arm/kernel/efi_phys.S > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index bf8e55d..022d2eb 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1763,6 +1763,19 @@ config EARLY_IOREMAP > Provides a mechanism for kernel initialisation code to temporarily > map, in a highmem-agnostic way, memory pages in before paging_init(). > > +config EFI > +bool "UEFI runtime service support" > + depends on OF > + select UCS2_STRING > + select EARLY_IOREMAP > + ---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 Be confident! s/should/will/ :-) > + continue to boot on existing non-UEFI platforms. s/existing// > + > config SECCOMP > bool > prompt "Enable seccomp to safely compute untrusted bytecode" > @@ -2217,6 +2230,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/efi.h b/arch/arm/include/asm/efi.h > new file mode 100644 > index 0000000..aead94c > --- /dev/null > +++ b/arch/arm/include/asm/efi.h > @@ -0,0 +1,22 @@ > +#ifndef _ASM_ARM_EFI_H > +#define _ASM_ARM_EFI_H > + > +#include <asm/mach/map.h> > + > +extern int efi_memblock_arm_reserve_range(void); > + > +typedef efi_status_t efi_phys_call_t(u32 memory_map_size, > + u32 descriptor_size, > + u32 descriptor_version, > + efi_memory_desc_t *dsc, > + efi_set_virtual_address_map_t *f); > + > +extern efi_status_t efi_phys_call(u32, u32, u32, efi_memory_desc_t *, > + efi_set_virtual_address_map_t *); > + > +#define efi_remap(cookie, size) __arm_ioremap((cookie), (size), MT_MEMORY) > +#define efi_ioremap(cookie, size) __arm_ioremap((cookie), (size), MT_DEVICE) > +#define efi_unmap(cookie) __arm_iounmap((cookie)) > +#define efi_iounmap(cookie) __arm_iounmap((cookie)) > + > +#endif /* _ASM_ARM_EFI_H */ > diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile > index 5f3338e..12b9c30 100644 > --- a/arch/arm/kernel/Makefile > +++ b/arch/arm/kernel/Makefile > @@ -84,4 +84,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o > obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o > obj-$(CONFIG_ARM_PSCI) += psci.o > > +obj-$(CONFIG_EFI) += efi.o efi_phys.o > + > extra-y := $(head-y) vmlinux.lds > diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c > new file mode 100644 > index 0000000..43ecf0b > --- /dev/null > +++ b/arch/arm/kernel/efi.c > @@ -0,0 +1,456 @@ > +/* > + * Extensible Firmware Interface > + * > + * Based on Extensible Firmware Interface Specification version 2.3.1 > + * > + * Copyright (C) 2013 Linaro Ltd. Was any of the above code extracted from the x86/ia64 efi code base? If so then make sure the copyright header and license text gets retained. > + * > + */ > + > +#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/efi.h> > +#include <asm/idmap.h> > +#include <asm/tlbflush.h> > + > +struct efi efi; > +EXPORT_SYMBOL(efi); > +struct efi_memory_map memmap; The above symbols should be pulled out of x86 and ia64 and made part of drivers/efi/efi.c. Although in my quick look I don't see memmap defined for ia64. You'll need to make sure that it actually exists before moving it. That will also affect your earlier patch which moves the memmap lookup function. I suspect that function won't build on ia64. > + > +static efi_runtime_services_t *runtime; > + > +static phys_addr_t efi_system_table; > +static phys_addr_t efi_boot_mmap; > +static u32 efi_boot_mmap_size; > +static u32 efi_mmap_desc_size; > +static u32 efi_mmap_desc_ver; > + > +/* Default memory map descriptor information */ > +#define DESC_SIZE 48 > +#define DESC_VER 1 > + > +/* If you're planning to wire up a debugger and debug the UEFI side ... */ > +#undef KEEP_ALL_REGIONS > +#define KEEP_BOOT_SERVICES_REGIONS > + > +static int __initdata uefi_debug; > +static int __init uefi_debug_setup(char *str) > +{ > + uefi_debug = 1; > + > + return 0; > +} > +early_param("uefi_debug", uefi_debug_setup); > + > +static int __init fdt_find_efi_params(unsigned long node, const char *uname, > + int depth, void *data) > +{ > + unsigned long len; > + __be32 *prop; > + > + if (depth != 1 || > + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) > + return 0; > + > + pr_info("Getting EFI parameters from FDT.\n"); > + > + prop = of_get_flat_dt_prop(node, "efi-system-table", &len); > + if (!prop) > + return 0; > + efi_system_table = of_read_ulong(prop, len/4); > + > + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap", &len); > + if (!prop) > + return 0; > + efi_boot_mmap = of_read_ulong(prop, len/4); > + > + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap-size", &len); > + if (!prop) > + return 0; > + efi_boot_mmap_size = of_read_ulong(prop, len/4); > + > + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-size", &len); > + if (prop) > + efi_mmap_desc_size = of_read_ulong(prop, len/4); > + else > + efi_mmap_desc_size = DESC_SIZE; > + > + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-ver", &len); > + if (prop) > + efi_mmap_desc_ver = of_read_ulong(prop, len/4); > + else > + efi_mmap_desc_ver = DESC_VER; As discussed in the binding patch, if the memmap is being passed in from GRUB, then it can be embedded directly into the device tree which will change the above code. > + > + if (uefi_debug) { > + pr_info(" EFI system table @ 0x%08x\n", > + (unsigned int) efi_system_table); > + pr_info(" EFI mmap @ 0x%08x\n", > + (unsigned int) efi_boot_mmap); > + pr_info(" EFI mmap size = 0x%08x\n", > + (unsigned int) efi_boot_mmap_size); > + pr_info(" EFI mmap descriptor size = 0x%08x\n", > + (unsigned int) efi_mmap_desc_size); > + pr_info(" EFI mmap descriptor version = 0x%08x\n", > + (unsigned int) efi_mmap_desc_ver); > + } > + > + return 1; > +} > + > +static int __init uefi_config_init(void) > +{ > + efi_char16_t *c16; > + char vendor[100] = "unknown"; > + efi_config_table_t *config_tables; > + u32 nr_tables; > + int i; > + > + efi.systab = early_ioremap(efi_system_table, > + sizeof(efi_system_table_t)); > + > + /* > + * Verify the EFI Table > + */ > + if (efi.systab == NULL) > + panic("Whoa! Can't find EFI system table.\n"); > + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) > + panic("Whoa! EFI system table signature incorrect\n"); > + if ((efi.systab->hdr.revision >> 16) == 0) > + pr_warn("Warning: EFI system table version %d.%02d, expected 1.00 or greater\n", > + efi.systab->hdr.revision >> 16, > + efi.systab->hdr.revision & 0xffff); > + > + /* Show what we know for posterity */ > + c16 = (efi_char16_t *)early_ioremap(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); > + > + > + nr_tables = efi.systab->nr_tables; > + > + config_tables = early_ioremap(efi.systab->tables, > + sizeof(efi_config_table_t) * nr_tables); > + > + for (i = 0; i < nr_tables; i++) { > + efi_guid_t guid; > + unsigned long table; > + u8 str[38]; > + > + guid = config_tables[i].guid; > + table = config_tables[i].table; > + > + efi_guid_unparse(&guid, str); > + if (uefi_debug) > + pr_info(" Config Table: UUID=%s @ 0x%08x\n", > + str, (u32)table); > + if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) > + efi.smbios = table; > + } > + > + early_iounmap(config_tables, sizeof(efi_config_table_t) * nr_tables); > + early_iounmap(c16, sizeof(vendor)); > + early_iounmap(efi.systab, sizeof(efi_system_table_t)); > + > + return 0; > +} > + > +static __init int is_discardable_region(efi_memory_desc_t *md) > +{ > +#ifdef KEEP_ALL_REGIONS > + return 0; > +#endif > + > + if (md->attribute & EFI_MEMORY_RUNTIME) > + return 0; > + > + switch (md->type) { > +#ifdef KEEP_BOOT_SERVICES_REGIONS > + case EFI_BOOT_SERVICES_CODE: > + case EFI_BOOT_SERVICES_DATA: > +#endif > + /* Keep tables around for any future kexec operations */ > + case EFI_ACPI_RECLAIM_MEMORY: > + return 0; > + } > + > + return 1; > +} > + > +static __initdata struct { > + u32 type; > + const char *name; > +} memory_type_name_map[] = { > + {EFI_RESERVED_TYPE, "EFI reserved"}, > + {EFI_LOADER_CODE, "EFI loader code"}, > + {EFI_LOADER_DATA, "EFI loader data"}, > + {EFI_BOOT_SERVICES_CODE, "EFI boot services code"}, > + {EFI_BOOT_SERVICES_DATA, "EFI boot services data"}, > + {EFI_RUNTIME_SERVICES_CODE, "EFI runtime services code"}, > + {EFI_RUNTIME_SERVICES_DATA, "EFI runtime services data"}, > + {EFI_CONVENTIONAL_MEMORY, "EFI conventional memory"}, > + {EFI_UNUSABLE_MEMORY, "EFI unusable memory"}, > + {EFI_ACPI_RECLAIM_MEMORY, "EFI ACPI reclaim memory"}, > + {EFI_ACPI_MEMORY_NVS, "EFI ACPI memory nvs"}, > + {EFI_MEMORY_MAPPED_IO, "EFI memory mapped I/O"}, > + {EFI_MEMORY_MAPPED_IO_PORT_SPACE, "EFI memory mapped I/O port space"}, > + {EFI_PAL_CODE, "EFI pal code"}, > + {EFI_MAX_MEMORY_TYPE, NULL}, > +}; The above *definitely* belongs in common code! :-) > + > +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 __init int remove_regions(void) > +{ > + efi_memory_desc_t *md; > + int count = 0; > + > + void *p; > + > + memmap.phys_map = early_ioremap(efi_boot_mmap, efi_boot_mmap_size); > + > + memmap.desc_size = efi_mmap_desc_size; > + memmap.desc_version = efi_mmap_desc_ver; > + memmap.map_end = (void *) memmap.phys_map + efi_boot_mmap_size; > + memmap.nr_map = 0; > + > + if (uefi_debug) > + pr_info("Processing EFI memory map:\n"); > + > + for (p = memmap.phys_map; p < memmap.map_end; p += memmap.desc_size) { > + md = p; > + if (is_discardable_region(md)) > + continue; > + > + if (uefi_debug) > + pr_info(" %8llu pages @ %016llx (%s)\n", > + md->num_pages, md->phys_addr, > + memory_type_name_map[md->type].name); > + > + if (md->type != EFI_MEMORY_MAPPED_IO) { > + remove_sections(md->phys_addr, > + md->num_pages * PAGE_SIZE); > + count++; > + } > + memmap.nr_map++; > + } > + > + if (uefi_debug) > + pr_info("%d regions preserved.\n", memmap.nr_map); > + > + early_iounmap(memmap.phys_map, efi_boot_mmap_size); > + > + return 0; > +} > + > +int __init efi_memblock_arm_reserve_range(void) > +{ > + /* Grab system table location out of FDT (or ACPI) */ > + of_scan_flat_dt(fdt_find_efi_params, NULL); > + > + if (!efi_system_table || !efi_boot_mmap || !efi_boot_mmap_size) > + return 0; > + > + remove_regions(); > + > + uefi_config_init(); > + > + return 0; > +} > + > +/* > + * Disable instrrupts, enable idmap and disable caches. > + */ > +static void __init phys_call_prologue(void) > +{ > + local_irq_disable(); > + > + /* 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(); > +} > + > +/* > + * 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); > + > + /* Flush branch predictor and TLBs */ > + local_flush_bp_all(); > +#ifdef CONFIG_CPU_HAS_ASID > + local_flush_tlb_all(); > +#endif > + > + local_irq_enable(); > +} > + > +/* > + * This function switches the EFI 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) > +{ > + efi_phys_call_t *phys_set_map; > + efi_status_t status; > + > + phys_call_prologue(); > + > + phys_set_map = (void *)(unsigned long)virt_to_phys(efi_phys_call); > + > + /* Called with caches disabled, returns with caches enabled */ > + status = phys_set_map(memory_map_size, descriptor_size, > + descriptor_version, dsc, > + efi.set_virtual_address_map); > + > + phys_call_epilogue(); > + > + return status; > +} Eventually we'll need to look at how this interacts with kexec. A kexec'd kernel will need to use the mapping already chosen by a previous kernel, but that's an issue for another patch series. The patch needs some rework, but otherwise looks good, although I'm concerned about duplicate code. However, I've not dug deep into the mmu handling code. Others more cluefull that I on the MMU details will need to weigh in on that. g.
On Tue, Jun 25, 2013 at 7:20 PM, Matthew Garrett <mjg59@srcf.ucam.org> wrote: > On Tue, Jun 25, 2013 at 07:11:02PM +0100, Leif Lindholm wrote: >> 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 also locates any presented SMBIOS configuration table and stores it >> for potential later use by DMI. > > This appears to duplicate code that's already duplicated between x86 and > ia64. We made a mistake there originally - let's not do it again. Having > this code in three places is inevitably going to lead to skew and missed > bugfixes. +1 > > -- > Matthew Garrett | mjg59@srcf.ucam.org > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/
On Wed, 26 Jun, at 02:46:09PM, Grant Likely wrote: > Eventually we'll need to look at how this interacts with kexec. A > kexec'd kernel will need to use the mapping already chosen by a > previous kernel, but that's an issue for another patch series. FYI, this is exactly what Borislav has been tackling on x86 recently. It would be nice if we could find one scheme that suits everyone.
On Wed, Jun 26, 2013 at 02:54:17PM +0100, Matt Fleming wrote: > On Wed, 26 Jun, at 02:46:09PM, Grant Likely wrote: > > Eventually we'll need to look at how this interacts with kexec. A > > kexec'd kernel will need to use the mapping already chosen by a > > previous kernel, but that's an issue for another patch series. > > FYI, this is exactly what Borislav has been tackling on x86 recently. It > would be nice if we could find one scheme that suits everyone. Is this arm 32 or 64-bit? Because we haven't talked about 32-bit on x86 either. From skimming over the code, I'm not sure the same top-down allocation and 1:1 mapping would work there. But I haven't looked hard yet so I dunno.
On Wed, Jun 26, 2013 at 02:46:09PM +0100, Grant Likely wrote: > > 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 also locates any presented SMBIOS configuration table and stores it > > for potential later use by DMI. > > > > Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org> > > --- > > arch/arm/Kconfig | 15 ++ > > arch/arm/include/asm/efi.h | 22 +++ > > arch/arm/kernel/Makefile | 2 + > > arch/arm/kernel/efi.c | 456 ++++++++++++++++++++++++++++++++++++++++++++ > > arch/arm/kernel/efi_phys.S | 59 ++++++ > > arch/arm/kernel/setup.c | 5 + > > 6 files changed, 559 insertions(+) > > create mode 100644 arch/arm/include/asm/efi.h > > create mode 100644 arch/arm/kernel/efi.c > > create mode 100644 arch/arm/kernel/efi_phys.S > > > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > > index bf8e55d..022d2eb 100644 > > --- a/arch/arm/Kconfig > > +++ b/arch/arm/Kconfig > > @@ -1763,6 +1763,19 @@ config EARLY_IOREMAP > > Provides a mechanism for kernel initialisation code to temporarily > > map, in a highmem-agnostic way, memory pages in before paging_init(). > > > > +config EFI > > +bool "UEFI runtime service support" > > + depends on OF > > + select UCS2_STRING > > + select EARLY_IOREMAP > > + ---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 > > Be confident! s/should/will/ :-) Ok. > > + continue to boot on existing non-UEFI platforms. > > s/existing// Ok. > > + > > config SECCOMP > > bool > > prompt "Enable seccomp to safely compute untrusted bytecode" > > @@ -2217,6 +2230,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/efi.h b/arch/arm/include/asm/efi.h > > new file mode 100644 > > index 0000000..aead94c > > --- /dev/null > > +++ b/arch/arm/include/asm/efi.h > > @@ -0,0 +1,22 @@ > > +#ifndef _ASM_ARM_EFI_H > > +#define _ASM_ARM_EFI_H > > + > > +#include <asm/mach/map.h> > > + > > +extern int efi_memblock_arm_reserve_range(void); > > + > > +typedef efi_status_t efi_phys_call_t(u32 memory_map_size, > > + u32 descriptor_size, > > + u32 descriptor_version, > > + efi_memory_desc_t *dsc, > > + efi_set_virtual_address_map_t *f); > > + > > +extern efi_status_t efi_phys_call(u32, u32, u32, efi_memory_desc_t *, > > + efi_set_virtual_address_map_t *); > > + > > +#define efi_remap(cookie, size) __arm_ioremap((cookie), (size), MT_MEMORY) > > +#define efi_ioremap(cookie, size) __arm_ioremap((cookie), (size), MT_DEVICE) > > +#define efi_unmap(cookie) __arm_iounmap((cookie)) > > +#define efi_iounmap(cookie) __arm_iounmap((cookie)) > > + > > +#endif /* _ASM_ARM_EFI_H */ > > diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile > > index 5f3338e..12b9c30 100644 > > --- a/arch/arm/kernel/Makefile > > +++ b/arch/arm/kernel/Makefile > > @@ -84,4 +84,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o > > obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o > > obj-$(CONFIG_ARM_PSCI) += psci.o > > > > +obj-$(CONFIG_EFI) += efi.o efi_phys.o > > + > > extra-y := $(head-y) vmlinux.lds > > diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c > > new file mode 100644 > > index 0000000..43ecf0b > > --- /dev/null > > +++ b/arch/arm/kernel/efi.c > > @@ -0,0 +1,456 @@ > > +/* > > + * Extensible Firmware Interface > > + * > > + * Based on Extensible Firmware Interface Specification version 2.3.1 > > + * > > + * Copyright (C) 2013 Linaro Ltd. > > Was any of the above code extracted from the x86/ia64 efi code base? > If so then make sure the copyright header and license text gets > retained. Well, some of efi_config_init() was, but it looks like I will be movng it out now. > > + * > > + */ > > + > > +#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/efi.h> > > +#include <asm/idmap.h> > > +#include <asm/tlbflush.h> > > + > > +struct efi efi; > > +EXPORT_SYMBOL(efi); > > +struct efi_memory_map memmap; > > The above symbols should be pulled out of x86 and ia64 and made part > of drivers/efi/efi.c. Although in my quick look I don't see memmap > defined for ia64. You'll need to make sure that it actually exists > before moving it. Happy to do that. Can be ifdef'd if need be. > That will also affect your earlier patch which moves > the memmap lookup function. I suspect that function won't build on > ia64. Argh, indeed. I had that ifdef'd ARM/X86, but it seems to have fallen out. > > + > > +static efi_runtime_services_t *runtime; > > + > > +static phys_addr_t efi_system_table; > > +static phys_addr_t efi_boot_mmap; > > +static u32 efi_boot_mmap_size; > > +static u32 efi_mmap_desc_size; > > +static u32 efi_mmap_desc_ver; > > + > > +/* Default memory map descriptor information */ > > +#define DESC_SIZE 48 > > +#define DESC_VER 1 > > + > > +/* If you're planning to wire up a debugger and debug the UEFI side ... */ > > +#undef KEEP_ALL_REGIONS > > +#define KEEP_BOOT_SERVICES_REGIONS > > + > > +static int __initdata uefi_debug; > > +static int __init uefi_debug_setup(char *str) > > +{ > > + uefi_debug = 1; > > + > > + return 0; > > +} > > +early_param("uefi_debug", uefi_debug_setup); > > + > > +static int __init fdt_find_efi_params(unsigned long node, const char *uname, > > + int depth, void *data) > > +{ > > + unsigned long len; > > + __be32 *prop; > > + > > + if (depth != 1 || > > + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) > > + return 0; > > + > > + pr_info("Getting EFI parameters from FDT.\n"); > > + > > + prop = of_get_flat_dt_prop(node, "efi-system-table", &len); > > + if (!prop) > > + return 0; > > + efi_system_table = of_read_ulong(prop, len/4); > > + > > + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap", &len); > > + if (!prop) > > + return 0; > > + efi_boot_mmap = of_read_ulong(prop, len/4); > > + > > + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap-size", &len); > > + if (!prop) > > + return 0; > > + efi_boot_mmap_size = of_read_ulong(prop, len/4); > > + > > + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-size", &len); > > + if (prop) > > + efi_mmap_desc_size = of_read_ulong(prop, len/4); > > + else > > + efi_mmap_desc_size = DESC_SIZE; > > + > > + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-ver", &len); > > + if (prop) > > + efi_mmap_desc_ver = of_read_ulong(prop, len/4); > > + else > > + efi_mmap_desc_ver = DESC_VER; > > As discussed in the binding patch, if the memmap is being passed in > from GRUB, then it can be embedded directly into the device tree which > will change the above code. Yes. > > + > > + if (uefi_debug) { > > + pr_info(" EFI system table @ 0x%08x\n", > > + (unsigned int) efi_system_table); > > + pr_info(" EFI mmap @ 0x%08x\n", > > + (unsigned int) efi_boot_mmap); > > + pr_info(" EFI mmap size = 0x%08x\n", > > + (unsigned int) efi_boot_mmap_size); > > + pr_info(" EFI mmap descriptor size = 0x%08x\n", > > + (unsigned int) efi_mmap_desc_size); > > + pr_info(" EFI mmap descriptor version = 0x%08x\n", > > + (unsigned int) efi_mmap_desc_ver); > > + } > > + > > + return 1; > > +} > > + > > +static int __init uefi_config_init(void) > > +{ > > + efi_char16_t *c16; > > + char vendor[100] = "unknown"; > > + efi_config_table_t *config_tables; > > + u32 nr_tables; > > + int i; > > + > > + efi.systab = early_ioremap(efi_system_table, > > + sizeof(efi_system_table_t)); > > + > > + /* > > + * Verify the EFI Table > > + */ > > + if (efi.systab == NULL) > > + panic("Whoa! Can't find EFI system table.\n"); > > + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) > > + panic("Whoa! EFI system table signature incorrect\n"); > > + if ((efi.systab->hdr.revision >> 16) == 0) > > + pr_warn("Warning: EFI system table version %d.%02d, expected 1.00 or greater\n", > > + efi.systab->hdr.revision >> 16, > > + efi.systab->hdr.revision & 0xffff); > > + > > + /* Show what we know for posterity */ > > + c16 = (efi_char16_t *)early_ioremap(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); > > + > > + > > + nr_tables = efi.systab->nr_tables; > > + > > + config_tables = early_ioremap(efi.systab->tables, > > + sizeof(efi_config_table_t) * nr_tables); > > + > > + for (i = 0; i < nr_tables; i++) { > > + efi_guid_t guid; > > + unsigned long table; > > + u8 str[38]; > > + > > + guid = config_tables[i].guid; > > + table = config_tables[i].table; > > + > > + efi_guid_unparse(&guid, str); > > + if (uefi_debug) > > + pr_info(" Config Table: UUID=%s @ 0x%08x\n", > > + str, (u32)table); > > + if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) > > + efi.smbios = table; > > + } > > + > > + early_iounmap(config_tables, sizeof(efi_config_table_t) * nr_tables); > > + early_iounmap(c16, sizeof(vendor)); > > + early_iounmap(efi.systab, sizeof(efi_system_table_t)); > > + > > + return 0; > > +} > > + > > +static __init int is_discardable_region(efi_memory_desc_t *md) > > +{ > > +#ifdef KEEP_ALL_REGIONS > > + return 0; > > +#endif > > + > > + if (md->attribute & EFI_MEMORY_RUNTIME) > > + return 0; > > + > > + switch (md->type) { > > +#ifdef KEEP_BOOT_SERVICES_REGIONS > > + case EFI_BOOT_SERVICES_CODE: > > + case EFI_BOOT_SERVICES_DATA: > > +#endif > > + /* Keep tables around for any future kexec operations */ > > + case EFI_ACPI_RECLAIM_MEMORY: > > + return 0; > > + } > > + > > + return 1; > > +} > > + > > +static __initdata struct { > > + u32 type; > > + const char *name; > > +} memory_type_name_map[] = { > > + {EFI_RESERVED_TYPE, "EFI reserved"}, > > + {EFI_LOADER_CODE, "EFI loader code"}, > > + {EFI_LOADER_DATA, "EFI loader data"}, > > + {EFI_BOOT_SERVICES_CODE, "EFI boot services code"}, > > + {EFI_BOOT_SERVICES_DATA, "EFI boot services data"}, > > + {EFI_RUNTIME_SERVICES_CODE, "EFI runtime services code"}, > > + {EFI_RUNTIME_SERVICES_DATA, "EFI runtime services data"}, > > + {EFI_CONVENTIONAL_MEMORY, "EFI conventional memory"}, > > + {EFI_UNUSABLE_MEMORY, "EFI unusable memory"}, > > + {EFI_ACPI_RECLAIM_MEMORY, "EFI ACPI reclaim memory"}, > > + {EFI_ACPI_MEMORY_NVS, "EFI ACPI memory nvs"}, > > + {EFI_MEMORY_MAPPED_IO, "EFI memory mapped I/O"}, > > + {EFI_MEMORY_MAPPED_IO_PORT_SPACE, "EFI memory mapped I/O port space"}, > > + {EFI_PAL_CODE, "EFI pal code"}, > > + {EFI_MAX_MEMORY_TYPE, NULL}, > > +}; > > The above *definitely* belongs in common code! :-) > > > + > > +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 __init int remove_regions(void) > > +{ > > + efi_memory_desc_t *md; > > + int count = 0; > > + > > + void *p; > > + > > + memmap.phys_map = early_ioremap(efi_boot_mmap, efi_boot_mmap_size); > > + > > + memmap.desc_size = efi_mmap_desc_size; > > + memmap.desc_version = efi_mmap_desc_ver; > > + memmap.map_end = (void *) memmap.phys_map + efi_boot_mmap_size; > > + memmap.nr_map = 0; > > + > > + if (uefi_debug) > > + pr_info("Processing EFI memory map:\n"); > > + > > + for (p = memmap.phys_map; p < memmap.map_end; p += memmap.desc_size) { > > + md = p; > > + if (is_discardable_region(md)) > > + continue; > > + > > + if (uefi_debug) > > + pr_info(" %8llu pages @ %016llx (%s)\n", > > + md->num_pages, md->phys_addr, > > + memory_type_name_map[md->type].name); > > + > > + if (md->type != EFI_MEMORY_MAPPED_IO) { > > + remove_sections(md->phys_addr, > > + md->num_pages * PAGE_SIZE); > > + count++; > > + } > > + memmap.nr_map++; > > + } > > + > > + if (uefi_debug) > > + pr_info("%d regions preserved.\n", memmap.nr_map); > > + > > + early_iounmap(memmap.phys_map, efi_boot_mmap_size); > > + > > + return 0; > > +} > > + > > +int __init efi_memblock_arm_reserve_range(void) > > +{ > > + /* Grab system table location out of FDT (or ACPI) */ > > + of_scan_flat_dt(fdt_find_efi_params, NULL); > > + > > + if (!efi_system_table || !efi_boot_mmap || !efi_boot_mmap_size) > > + return 0; > > + > > + remove_regions(); > > + > > + uefi_config_init(); > > + > > + return 0; > > +} > > + > > +/* > > + * Disable instrrupts, enable idmap and disable caches. > > + */ > > +static void __init phys_call_prologue(void) > > +{ > > + local_irq_disable(); > > + > > + /* 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(); > > +} > > + > > +/* > > + * 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); > > + > > + /* Flush branch predictor and TLBs */ > > + local_flush_bp_all(); > > +#ifdef CONFIG_CPU_HAS_ASID > > + local_flush_tlb_all(); > > +#endif > > + > > + local_irq_enable(); > > +} > > + > > +/* > > + * This function switches the EFI 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) > > +{ > > + efi_phys_call_t *phys_set_map; > > + efi_status_t status; > > + > > + phys_call_prologue(); > > + > > + phys_set_map = (void *)(unsigned long)virt_to_phys(efi_phys_call); > > + > > + /* Called with caches disabled, returns with caches enabled */ > > + status = phys_set_map(memory_map_size, descriptor_size, > > + descriptor_version, dsc, > > + efi.set_virtual_address_map); > > + > > + phys_call_epilogue(); > > + > > + return status; > > +} > > Eventually we'll need to look at how this interacts with kexec. A > kexec'd kernel will need to use the mapping already chosen by a > previous kernel, but that's an issue for another patch series. Yes please :] > The patch needs some rework, but otherwise looks good, although I'm > concerned about duplicate code. However, I've not dug deep into the > mmu handling code. Others more cluefull that I on the MMU details will > need to weigh in on that. Thanks. / Leif
On Wed, Jun 26, 2013 at 3:15 PM, Borislav Petkov <bp@alien8.de> wrote: > On Wed, Jun 26, 2013 at 02:54:17PM +0100, Matt Fleming wrote: >> On Wed, 26 Jun, at 02:46:09PM, Grant Likely wrote: >> > Eventually we'll need to look at how this interacts with kexec. A >> > kexec'd kernel will need to use the mapping already chosen by a >> > previous kernel, but that's an issue for another patch series. >> >> FYI, this is exactly what Borislav has been tackling on x86 recently. It >> would be nice if we could find one scheme that suits everyone. > > Is this arm 32 or 64-bit? Because we haven't talked about 32-bit on x86 > either. From skimming over the code, I'm not sure the same top-down > allocation and 1:1 mapping would work there. But I haven't looked hard > yet so I dunno. This is arm 32. We'll be looking at arm 64 next. g.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bf8e55d..022d2eb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1763,6 +1763,19 @@ config EARLY_IOREMAP Provides a mechanism for kernel initialisation code to temporarily map, in a highmem-agnostic way, memory pages in before paging_init(). +config EFI +bool "UEFI runtime service support" + depends on OF + select UCS2_STRING + select EARLY_IOREMAP + ---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 SECCOMP bool prompt "Enable seccomp to safely compute untrusted bytecode" @@ -2217,6 +2230,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/efi.h b/arch/arm/include/asm/efi.h new file mode 100644 index 0000000..aead94c --- /dev/null +++ b/arch/arm/include/asm/efi.h @@ -0,0 +1,22 @@ +#ifndef _ASM_ARM_EFI_H +#define _ASM_ARM_EFI_H + +#include <asm/mach/map.h> + +extern int efi_memblock_arm_reserve_range(void); + +typedef efi_status_t efi_phys_call_t(u32 memory_map_size, + u32 descriptor_size, + u32 descriptor_version, + efi_memory_desc_t *dsc, + efi_set_virtual_address_map_t *f); + +extern efi_status_t efi_phys_call(u32, u32, u32, efi_memory_desc_t *, + efi_set_virtual_address_map_t *); + +#define efi_remap(cookie, size) __arm_ioremap((cookie), (size), MT_MEMORY) +#define efi_ioremap(cookie, size) __arm_ioremap((cookie), (size), MT_DEVICE) +#define efi_unmap(cookie) __arm_iounmap((cookie)) +#define efi_iounmap(cookie) __arm_iounmap((cookie)) + +#endif /* _ASM_ARM_EFI_H */ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 5f3338e..12b9c30 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -84,4 +84,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o obj-$(CONFIG_ARM_PSCI) += psci.o +obj-$(CONFIG_EFI) += efi.o efi_phys.o + extra-y := $(head-y) vmlinux.lds diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c new file mode 100644 index 0000000..43ecf0b --- /dev/null +++ b/arch/arm/kernel/efi.c @@ -0,0 +1,456 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.3.1 + * + * Copyright (C) 2013 Linaro Ltd. + * + */ + +#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/efi.h> +#include <asm/idmap.h> +#include <asm/tlbflush.h> + +struct efi efi; +EXPORT_SYMBOL(efi); +struct efi_memory_map memmap; + +static efi_runtime_services_t *runtime; + +static phys_addr_t efi_system_table; +static phys_addr_t efi_boot_mmap; +static u32 efi_boot_mmap_size; +static u32 efi_mmap_desc_size; +static u32 efi_mmap_desc_ver; + +/* Default memory map descriptor information */ +#define DESC_SIZE 48 +#define DESC_VER 1 + +/* If you're planning to wire up a debugger and debug the UEFI side ... */ +#undef KEEP_ALL_REGIONS +#define KEEP_BOOT_SERVICES_REGIONS + +static int __initdata uefi_debug; +static int __init uefi_debug_setup(char *str) +{ + uefi_debug = 1; + + return 0; +} +early_param("uefi_debug", uefi_debug_setup); + +static int __init fdt_find_efi_params(unsigned long node, const char *uname, + int depth, void *data) +{ + unsigned long len; + __be32 *prop; + + if (depth != 1 || + (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) + return 0; + + pr_info("Getting EFI parameters from FDT.\n"); + + prop = of_get_flat_dt_prop(node, "efi-system-table", &len); + if (!prop) + return 0; + efi_system_table = of_read_ulong(prop, len/4); + + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap", &len); + if (!prop) + return 0; + efi_boot_mmap = of_read_ulong(prop, len/4); + + prop = of_get_flat_dt_prop(node, "efi-runtime-mmap-size", &len); + if (!prop) + return 0; + efi_boot_mmap_size = of_read_ulong(prop, len/4); + + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-size", &len); + if (prop) + efi_mmap_desc_size = of_read_ulong(prop, len/4); + else + efi_mmap_desc_size = DESC_SIZE; + + prop = of_get_flat_dt_prop(node, "efi-mmap-desc-ver", &len); + if (prop) + efi_mmap_desc_ver = of_read_ulong(prop, len/4); + else + efi_mmap_desc_ver = DESC_VER; + + if (uefi_debug) { + pr_info(" EFI system table @ 0x%08x\n", + (unsigned int) efi_system_table); + pr_info(" EFI mmap @ 0x%08x\n", + (unsigned int) efi_boot_mmap); + pr_info(" EFI mmap size = 0x%08x\n", + (unsigned int) efi_boot_mmap_size); + pr_info(" EFI mmap descriptor size = 0x%08x\n", + (unsigned int) efi_mmap_desc_size); + pr_info(" EFI mmap descriptor version = 0x%08x\n", + (unsigned int) efi_mmap_desc_ver); + } + + return 1; +} + +static int __init uefi_config_init(void) +{ + efi_char16_t *c16; + char vendor[100] = "unknown"; + efi_config_table_t *config_tables; + u32 nr_tables; + int i; + + efi.systab = early_ioremap(efi_system_table, + sizeof(efi_system_table_t)); + + /* + * Verify the EFI Table + */ + if (efi.systab == NULL) + panic("Whoa! Can't find EFI system table.\n"); + if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + panic("Whoa! EFI system table signature incorrect\n"); + if ((efi.systab->hdr.revision >> 16) == 0) + pr_warn("Warning: EFI system table version %d.%02d, expected 1.00 or greater\n", + efi.systab->hdr.revision >> 16, + efi.systab->hdr.revision & 0xffff); + + /* Show what we know for posterity */ + c16 = (efi_char16_t *)early_ioremap(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); + + + nr_tables = efi.systab->nr_tables; + + config_tables = early_ioremap(efi.systab->tables, + sizeof(efi_config_table_t) * nr_tables); + + for (i = 0; i < nr_tables; i++) { + efi_guid_t guid; + unsigned long table; + u8 str[38]; + + guid = config_tables[i].guid; + table = config_tables[i].table; + + efi_guid_unparse(&guid, str); + if (uefi_debug) + pr_info(" Config Table: UUID=%s @ 0x%08x\n", + str, (u32)table); + if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) + efi.smbios = table; + } + + early_iounmap(config_tables, sizeof(efi_config_table_t) * nr_tables); + early_iounmap(c16, sizeof(vendor)); + early_iounmap(efi.systab, sizeof(efi_system_table_t)); + + return 0; +} + +static __init int is_discardable_region(efi_memory_desc_t *md) +{ +#ifdef KEEP_ALL_REGIONS + return 0; +#endif + + if (md->attribute & EFI_MEMORY_RUNTIME) + return 0; + + switch (md->type) { +#ifdef KEEP_BOOT_SERVICES_REGIONS + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: +#endif + /* Keep tables around for any future kexec operations */ + case EFI_ACPI_RECLAIM_MEMORY: + return 0; + } + + return 1; +} + +static __initdata struct { + u32 type; + const char *name; +} memory_type_name_map[] = { + {EFI_RESERVED_TYPE, "EFI reserved"}, + {EFI_LOADER_CODE, "EFI loader code"}, + {EFI_LOADER_DATA, "EFI loader data"}, + {EFI_BOOT_SERVICES_CODE, "EFI boot services code"}, + {EFI_BOOT_SERVICES_DATA, "EFI boot services data"}, + {EFI_RUNTIME_SERVICES_CODE, "EFI runtime services code"}, + {EFI_RUNTIME_SERVICES_DATA, "EFI runtime services data"}, + {EFI_CONVENTIONAL_MEMORY, "EFI conventional memory"}, + {EFI_UNUSABLE_MEMORY, "EFI unusable memory"}, + {EFI_ACPI_RECLAIM_MEMORY, "EFI ACPI reclaim memory"}, + {EFI_ACPI_MEMORY_NVS, "EFI ACPI memory nvs"}, + {EFI_MEMORY_MAPPED_IO, "EFI memory mapped I/O"}, + {EFI_MEMORY_MAPPED_IO_PORT_SPACE, "EFI memory mapped I/O port space"}, + {EFI_PAL_CODE, "EFI 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 __init int remove_regions(void) +{ + efi_memory_desc_t *md; + int count = 0; + + void *p; + + memmap.phys_map = early_ioremap(efi_boot_mmap, efi_boot_mmap_size); + + memmap.desc_size = efi_mmap_desc_size; + memmap.desc_version = efi_mmap_desc_ver; + memmap.map_end = (void *) memmap.phys_map + efi_boot_mmap_size; + memmap.nr_map = 0; + + if (uefi_debug) + pr_info("Processing EFI memory map:\n"); + + for (p = memmap.phys_map; p < memmap.map_end; p += memmap.desc_size) { + md = p; + if (is_discardable_region(md)) + continue; + + if (uefi_debug) + pr_info(" %8llu pages @ %016llx (%s)\n", + md->num_pages, md->phys_addr, + memory_type_name_map[md->type].name); + + if (md->type != EFI_MEMORY_MAPPED_IO) { + remove_sections(md->phys_addr, + md->num_pages * PAGE_SIZE); + count++; + } + memmap.nr_map++; + } + + if (uefi_debug) + pr_info("%d regions preserved.\n", memmap.nr_map); + + early_iounmap(memmap.phys_map, efi_boot_mmap_size); + + return 0; +} + +int __init efi_memblock_arm_reserve_range(void) +{ + /* Grab system table location out of FDT (or ACPI) */ + of_scan_flat_dt(fdt_find_efi_params, NULL); + + if (!efi_system_table || !efi_boot_mmap || !efi_boot_mmap_size) + return 0; + + remove_regions(); + + uefi_config_init(); + + return 0; +} + +/* + * Disable instrrupts, enable idmap and disable caches. + */ +static void __init phys_call_prologue(void) +{ + local_irq_disable(); + + /* 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(); +} + +/* + * 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); + + /* Flush branch predictor and TLBs */ + local_flush_bp_all(); +#ifdef CONFIG_CPU_HAS_ASID + local_flush_tlb_all(); +#endif + + local_irq_enable(); +} + +/* + * This function switches the EFI 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) +{ + efi_phys_call_t *phys_set_map; + efi_status_t status; + + phys_call_prologue(); + + phys_set_map = (void *)(unsigned long)virt_to_phys(efi_phys_call); + + /* Called with caches disabled, returns with caches enabled */ + status = phys_set_map(memory_map_size, descriptor_size, + descriptor_version, dsc, + efi.set_virtual_address_map); + + phys_call_epilogue(); + + return status; +} + +static int __init remap_region(efi_memory_desc_t *md, efi_memory_desc_t *entry) +{ + u64 va; + u64 paddr; + u64 size; + + *entry = *md; + paddr = entry->phys_addr; + size = entry->num_pages << EFI_PAGE_SHIFT; + + /* + * Map everything writeback-capable as coherent memory, + * anything else as device. + */ + if (md->attribute & EFI_MEMORY_WB) + va = (u64)((u32)efi_remap(paddr, size) & 0xffffffffUL); + else + va = (u64)((u32)efi_ioremap(paddr, size) & 0xffffffffUL); + if (!va) + return 0; + entry->virt_addr = va; + + if (uefi_debug) + pr_info(" %016llx-%016llx => 0x%08x : (%s)\n", + paddr, paddr + size - 1, (u32)va, + md->attribute & EFI_MEMORY_WB ? "WB" : "I/O"); + + return 1; +} + +static void __init remap_regions(void) +{ + void *p, *next; + efi_memory_desc_t *md; + + memmap.phys_map = efi_remap(efi_boot_mmap, efi_boot_mmap_size); + memmap.map_end = (void *)memmap.phys_map + efi_boot_mmap_size; + + /* Allocate space for the physical region map */ + memmap.map = kzalloc(memmap.nr_map * memmap.desc_size, GFP_KERNEL); + if (!memmap.map) + return; + + next = memmap.map; + 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, next)) + next += memmap.desc_size; + } + + memmap.map_end = next; + + efi_unmap(memmap.phys_map); + + efi.systab = efi_lookup_mapped_addr(efi_system_table); + /* + * 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); +} + + +/* + * Called explicitly from init/mm.c + */ +void __init efi_enter_virtual_mode(void) +{ + efi_status_t status; + + if (efi.systab == NULL) { + pr_info("No EFI system table - EFI services will not be available.\n"); + return; + } else { + pr_info("Remapping and enabling EFI services.\n"); + } + + /* Map the regions we memblock_remove:d earlier into kernel + address space */ + remap_regions(); + + /* Call SetVirtualAddressMap with the physical address of the map */ + efi.set_virtual_address_map = + (efi_set_virtual_address_map_t *) + runtime->set_virtual_address_map; + memmap.phys_map = + (efi_memory_desc_t *)(u32) __virt_to_phys((u32)memmap.map); + + status = phys_set_virtual_address_map(memmap.nr_map * memmap.desc_size, + memmap.desc_size, + memmap.desc_version, + memmap.phys_map); + + /* Set up function pointers for efivars */ + efi.get_variable = (efi_get_variable_t *)runtime->get_variable; + efi.get_next_variable = + (efi_get_next_variable_t *)runtime->get_next_variable; + efi.set_variable = (efi_set_variable_t *)runtime->set_variable; +} diff --git a/arch/arm/kernel/efi_phys.S b/arch/arm/kernel/efi_phys.S new file mode 100644 index 0000000..e36cc17 --- /dev/null +++ b/arch/arm/kernel/efi_phys.S @@ -0,0 +1,59 @@ +/* + * arch/arm/kernel/efi_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 <linux/linkage.h> +#define PAR_MASK 0xfff + + .text +@ efi_phys_call(a, b, c, d, *f) + .align 5 + .pushsection .idmap.text, "ax" +ENTRY(efi_phys_call) + @ Save physical context + mov r12, sp + push {r4-r5, r12, lr} + + @ Extract function pointer (don't write r12 beyond this) + ldr r12, [sp, #16] + + @ Convert sp to 32-bit physical + mov lr, sp + ldr r4, =PAR_MASK + and r5, lr, r4 @ Extract lower 12 bits of sp + mcr p15, 0, lr, c7, c8, 1 @ Write VA -> ATS1CPW + mrc p15, 0, lr, c7, c4, 0 @ Physical Address Register + mvn r4, r4 + and lr, lr, r4 @ Clear lower 12 bits of PA + add lr, lr, r5 @ Calculate phys sp + mov sp, lr @ Update + + @ Disable MMU + mrc p15, 0, lr, c1, c0, 0 @ ctrl register + bic lr, lr, #0x1 @ ...............m + mcr p15, 0, lr, c1, c0, 0 @ disable MMU + isb + + @ Make call + blx r12 + + pop {r4-r5, r12, lr} + + @ Enable MMU + Caches + mrc p15, 0, r1, c1, c0, 0 @ ctrl register + orr r1, r1, #0x1000 @ ...i............ + orr r1, r1, #0x0005 @ .............c.m + mcr p15, 0, r1, c1, c0, 0 @ enable MMU + isb + + @ Restore virtual sp and return + mov sp, r12 + bx lr +ENDPROC(efi_phys_call) + .popsection diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 290c561..e36c2a6 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> @@ -56,6 +57,7 @@ #include <asm/unwind.h> #include <asm/memblock.h> #include <asm/virt.h> +#include <asm/efi.h> #include "atags.h" @@ -790,6 +792,9 @@ void __init setup_arch(char **cmdline_p) sanity_check_meminfo(); arm_memblock_init(&meminfo, mdesc); + if (efi_enabled(EFI_BOOT)) + efi_memblock_arm_reserve_range(); + paging_init(mdesc); request_standard_resources(mdesc);
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 also locates any presented SMBIOS configuration table and stores it for potential later use by DMI. Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org> --- arch/arm/Kconfig | 15 ++ arch/arm/include/asm/efi.h | 22 +++ arch/arm/kernel/Makefile | 2 + arch/arm/kernel/efi.c | 456 ++++++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/efi_phys.S | 59 ++++++ arch/arm/kernel/setup.c | 5 + 6 files changed, 559 insertions(+) create mode 100644 arch/arm/include/asm/efi.h create mode 100644 arch/arm/kernel/efi.c create mode 100644 arch/arm/kernel/efi_phys.S