From 6f9dd30c22da4e48c4b7b837e9641f072e673161 Mon Sep 17 00:00:00 2001 From: Bojan Prtvar Date: Tue, 3 Sep 2013 08:56:20 +0200 Subject: [PATCH 01/26] efivars: Mark local function as static This fixes the following sparse warning drivers/firmware/efi/efivars.c:567:6: warning: symbol 'efivars_sysfs_exit' was not declared. Should it be static? Signed-off-by: Bojan Prtvar Signed-off-by: Matt Fleming --- drivers/firmware/efi/efivars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 8a7432a4b413..933eb027d527 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -564,7 +564,7 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) return 0; } -void efivars_sysfs_exit(void) +static void efivars_sysfs_exit(void) { /* Remove all entries and destroy */ __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL); From d02d0545f1fc62302fd9973a530b8029f1d9a9f1 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:53 +0100 Subject: [PATCH 02/26] ia64: add early_memremap() alias for early_ioremap() early_ioremap() on IA64 chooses its mapping type based on the EFI memory map. This patch adds an alias "early_memremap()" to be used where the targeted location is memory rather than an i/o device. Signed-off-by: Leif Lindholm Acked-by: Tony Luck Signed-off-by: Matt Fleming --- arch/ia64/include/asm/io.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h index 74a7cc3293bc..0d2bcb37ec35 100644 --- a/arch/ia64/include/asm/io.h +++ b/arch/ia64/include/asm/io.h @@ -424,6 +424,7 @@ extern void __iomem * ioremap(unsigned long offset, unsigned long size); extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size); extern void iounmap (volatile void __iomem *addr); extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size); +#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size) extern void early_iounmap (volatile void __iomem *addr, unsigned long size); static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size) { From 272686bf46a34f86d270cf192f68769667792026 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:54 +0100 Subject: [PATCH 03/26] efi: x86: ia64: provide a generic efi_config_init() Common to (U)EFI support on all platforms is the global "efi" data structure, and the code that parses the System Table to locate addresses to populate that structure with. This patch adds both of these to the global EFI driver code and removes the local definition of the global "efi" data structure from the x86 and ia64 code. Squashed into one big patch to avoid breaking bisection. Signed-off-by: Leif Lindholm Acked-by: Tony Luck Signed-off-by: Matt Fleming --- arch/ia64/kernel/efi.c | 54 ++++-------------- arch/x86/platform/efi/efi.c | 96 +++----------------------------- drivers/firmware/efi/efi.c | 108 ++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 7 +++ 4 files changed, 134 insertions(+), 131 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 51bce594eb83..da5b462e6de6 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -44,10 +44,15 @@ #define EFI_DEBUG 0 +static __initdata unsigned long palo_phys; + +static __initdata efi_config_table_type_t arch_tables[] = { + {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, + {NULL_GUID, NULL, 0}, +}; + extern efi_status_t efi_call_phys (void *, ...); -struct efi efi; -EXPORT_SYMBOL(efi); static efi_runtime_services_t *runtime; static u64 mem_limit = ~0UL, max_addr = ~0UL, min_addr = 0UL; @@ -423,9 +428,9 @@ static u8 __init palo_checksum(u8 *buffer, u32 length) * Parse and handle PALO table which is published at: * http://www.dig64.org/home/DIG64_PALO_R1_0.pdf */ -static void __init handle_palo(unsigned long palo_phys) +static void __init handle_palo(unsigned long phys_addr) { - struct palo_table *palo = __va(palo_phys); + struct palo_table *palo = __va(phys_addr); u8 checksum; if (strncmp(palo->signature, PALO_SIG, sizeof(PALO_SIG) - 1)) { @@ -467,12 +472,10 @@ void __init efi_init (void) { void *efi_map_start, *efi_map_end; - efi_config_table_t *config_tables; efi_char16_t *c16; u64 efi_desc_size; char *cp, vendor[100] = "unknown"; int i; - unsigned long palo_phys; /* * It's too early to be able to use the standard kernel command line @@ -514,8 +517,6 @@ efi_init (void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff); - config_tables = __va(efi.systab->tables); - /* Show what we know for posterity */ c16 = __va(efi.systab->fw_vendor); if (c16) { @@ -528,43 +529,10 @@ efi_init (void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - efi.mps = EFI_INVALID_TABLE_ADDR; - efi.acpi = EFI_INVALID_TABLE_ADDR; - efi.acpi20 = EFI_INVALID_TABLE_ADDR; - efi.smbios = EFI_INVALID_TABLE_ADDR; - efi.sal_systab = EFI_INVALID_TABLE_ADDR; - efi.boot_info = EFI_INVALID_TABLE_ADDR; - efi.hcdp = EFI_INVALID_TABLE_ADDR; - efi.uga = EFI_INVALID_TABLE_ADDR; - palo_phys = EFI_INVALID_TABLE_ADDR; - for (i = 0; i < (int) efi.systab->nr_tables; i++) { - if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { - efi.mps = config_tables[i].table; - printk(" MPS=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { - efi.acpi20 = config_tables[i].table; - printk(" ACPI 2.0=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { - efi.acpi = config_tables[i].table; - printk(" ACPI=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = config_tables[i].table; - printk(" SMBIOS=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { - efi.sal_systab = config_tables[i].table; - printk(" SALsystab=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { - efi.hcdp = config_tables[i].table; - printk(" HCDP=0x%lx", config_tables[i].table); - } else if (efi_guidcmp(config_tables[i].guid, - PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID) == 0) { - palo_phys = config_tables[i].table; - printk(" PALO=0x%lx", config_tables[i].table); - } - } - printk("\n"); + if (efi_config_init(arch_tables) != 0) + return; if (palo_phys != EFI_INVALID_TABLE_ADDR) handle_palo(palo_phys); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 90f6ed127096..ed2be58ea3f1 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -60,19 +60,6 @@ static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 }; -struct efi __read_mostly efi = { - .mps = EFI_INVALID_TABLE_ADDR, - .acpi = EFI_INVALID_TABLE_ADDR, - .acpi20 = EFI_INVALID_TABLE_ADDR, - .smbios = EFI_INVALID_TABLE_ADDR, - .sal_systab = EFI_INVALID_TABLE_ADDR, - .boot_info = EFI_INVALID_TABLE_ADDR, - .hcdp = EFI_INVALID_TABLE_ADDR, - .uga = EFI_INVALID_TABLE_ADDR, - .uv_systab = EFI_INVALID_TABLE_ADDR, -}; -EXPORT_SYMBOL(efi); - struct efi_memory_map memmap; static struct efi efi_phys __initdata; @@ -80,6 +67,13 @@ static efi_system_table_t efi_systab __initdata; unsigned long x86_efi_facility; +static __initdata efi_config_table_type_t arch_tables[] = { +#ifdef CONFIG_X86_UV + {UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab}, +#endif + {NULL_GUID, NULL, 0}, +}; + /* * Returns 1 if 'facility' is enabled, 0 otherwise. */ @@ -578,80 +572,6 @@ static int __init efi_systab_init(void *phys) return 0; } -static int __init efi_config_init(u64 tables, int nr_tables) -{ - void *config_tables, *tablep; - int i, sz; - - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); - - /* - * Let's see what config tables the firmware passed to us. - */ - config_tables = early_ioremap(tables, nr_tables * sz); - if (config_tables == NULL) { - pr_err("Could not map Configuration table!\n"); - return -ENOMEM; - } - - tablep = config_tables; - pr_info(""); - for (i = 0; i < efi.systab->nr_tables; i++) { - efi_guid_t guid; - unsigned long table; - - if (efi_enabled(EFI_64BIT)) { - u64 table64; - guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; -#ifdef CONFIG_X86_32 - if (table64 >> 32) { - pr_cont("\n"); - pr_err("Table located above 4GB, disabling EFI.\n"); - early_iounmap(config_tables, - efi.systab->nr_tables * sz); - return -EINVAL; - } -#endif - } else { - guid = ((efi_config_table_32_t *)tablep)->guid; - table = ((efi_config_table_32_t *)tablep)->table; - } - if (!efi_guidcmp(guid, MPS_TABLE_GUID)) { - efi.mps = table; - pr_cont(" MPS=0x%lx ", table); - } else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) { - efi.acpi20 = table; - pr_cont(" ACPI 2.0=0x%lx ", table); - } else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) { - efi.acpi = table; - pr_cont(" ACPI=0x%lx ", table); - } else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) { - efi.smbios = table; - pr_cont(" SMBIOS=0x%lx ", table); -#ifdef CONFIG_X86_UV - } else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) { - efi.uv_systab = table; - pr_cont(" UVsystab=0x%lx ", table); -#endif - } else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) { - efi.hcdp = table; - pr_cont(" HCDP=0x%lx ", table); - } else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) { - efi.uga = table; - pr_cont(" UGA=0x%lx ", table); - } - tablep += sz; - } - pr_cont("\n"); - early_iounmap(config_tables, efi.systab->nr_tables * sz); - return 0; -} - static int __init efi_runtime_init(void) { efi_runtime_services_t *runtime; @@ -745,7 +665,7 @@ void __init efi_init(void) efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) + if (efi_config_init(arch_tables)) return; set_bit(EFI_CONFIG_TABLES, &x86_efi_facility); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 5145fa344ad5..e1010d450b65 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -13,11 +13,27 @@ * This file is released under the GPLv2. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include #include #include +#include + +struct efi __read_mostly efi = { + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .sal_systab = EFI_INVALID_TABLE_ADDR, + .boot_info = EFI_INVALID_TABLE_ADDR, + .hcdp = EFI_INVALID_TABLE_ADDR, + .uga = EFI_INVALID_TABLE_ADDR, + .uv_systab = EFI_INVALID_TABLE_ADDR, +}; +EXPORT_SYMBOL(efi); static struct kobject *efi_kobj; static struct kobject *efivars_kobj; @@ -132,3 +148,95 @@ err_put: } subsys_initcall(efisubsys_init); + + +static __initdata efi_config_table_type_t common_tables[] = { + {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, + {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, + {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, + {MPS_TABLE_GUID, "MPS", &efi.mps}, + {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, + {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, + {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, + {NULL_GUID, NULL, 0}, +}; + +static __init int match_config_table(efi_guid_t *guid, + unsigned long table, + efi_config_table_type_t *table_types) +{ + u8 str[EFI_VARIABLE_GUID_LEN + 1]; + int i; + + if (table_types) { + efi_guid_unparse(guid, str); + + for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { + efi_guid_unparse(&table_types[i].guid, str); + + if (!efi_guidcmp(*guid, table_types[i].guid)) { + *(table_types[i].ptr) = table; + pr_cont(" %s=0x%lx ", + table_types[i].name, table); + return 1; + } + } + } + + return 0; +} + +int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ + void *config_tables, *tablep; + int i, sz; + + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = early_memremap(efi.systab->tables, + efi.systab->nr_tables * sz); + if (config_tables == NULL) { + pr_err("Could not map Configuration table!\n"); + return -ENOMEM; + } + + tablep = config_tables; + pr_info(""); + for (i = 0; i < efi.systab->nr_tables; i++) { + efi_guid_t guid; + unsigned long table; + + if (efi_enabled(EFI_64BIT)) { + u64 table64; + guid = ((efi_config_table_64_t *)tablep)->guid; + table64 = ((efi_config_table_64_t *)tablep)->table; + table = table64; +#ifndef CONFIG_64BIT + if (table64 >> 32) { + pr_cont("\n"); + pr_err("Table located above 4GB, disabling EFI.\n"); + early_iounmap(config_tables, + efi.systab->nr_tables * sz); + return -EINVAL; + } +#endif + } else { + guid = ((efi_config_table_32_t *)tablep)->guid; + table = ((efi_config_table_32_t *)tablep)->table; + } + + if (!match_config_table(&guid, table, common_tables)) + match_config_table(&guid, table, arch_tables); + + tablep += sz; + } + pr_cont("\n"); + early_iounmap(config_tables, efi.systab->nr_tables * sz); + return 0; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 5f8f176154f7..09d9e4212799 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -404,6 +404,12 @@ typedef struct { unsigned long table; } efi_config_table_t; +typedef struct { + efi_guid_t guid; + const char *name; + unsigned long *ptr; +} efi_config_table_type_t; + #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) #define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) @@ -587,6 +593,7 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon } #endif extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); +extern int efi_config_init(efi_config_table_type_t *arch_tables); extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); From 258f6fd738221766b512cd8c7120563b78d62829 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:55 +0100 Subject: [PATCH 04/26] efi: x86: make efi_lookup_mapped_addr() a common function efi_lookup_mapped_addr() is a handy utility for other platforms than x86. Move it from arch/x86 to drivers/firmware. Add memmap pointer to global efi structure, and initialise it on x86. Signed-off-by: Leif Lindholm Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 30 ++---------------------------- drivers/firmware/efi/efi.c | 32 ++++++++++++++++++++++++++++++++ include/linux/efi.h | 1 + 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ed2be58ea3f1..fbc1d70188f8 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -393,6 +393,8 @@ int __init efi_memblock_x86_reserve_range(void) memblock_reserve(pmap, memmap.nr_map * memmap.desc_size); + efi.memmap = &memmap; + return 0; } @@ -736,34 +738,6 @@ static void __init runtime_code_page_mkexec(void) } } -/* - * We can't ioremap data in EFI boot services RAM, because we've already mapped - * it as RAM. So, look it up in the existing EFI memory map instead. Only - * callable after efi_enter_virtual_mode and before efi_free_boot_services. - */ -void __iomem *efi_lookup_mapped_addr(u64 phys_addr) -{ - void *p; - if (WARN_ON(!memmap.map)) - return NULL; - for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { - efi_memory_desc_t *md = p; - u64 size = md->num_pages << EFI_PAGE_SHIFT; - u64 end = md->phys_addr + size; - if (!(md->attribute & EFI_MEMORY_RUNTIME) && - md->type != EFI_BOOT_SERVICES_CODE && - md->type != EFI_BOOT_SERVICES_DATA) - continue; - if (!md->virt_addr) - continue; - if (phys_addr >= md->phys_addr && phys_addr < end) { - phys_addr += md->virt_addr - md->phys_addr; - return (__force void __iomem *)(unsigned long)phys_addr; - } - } - return NULL; -} - void efi_memory_uc(u64 addr, unsigned long size) { unsigned long page_shift = 1UL << EFI_PAGE_SHIFT; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index e1010d450b65..2e2fbdec0845 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -150,6 +150,38 @@ err_put: subsys_initcall(efisubsys_init); +/* + * We can't ioremap data in EFI boot services RAM, because we've already mapped + * it as RAM. So, look it up in the existing EFI memory map instead. Only + * callable after efi_enter_virtual_mode and before efi_free_boot_services. + */ +void __iomem *efi_lookup_mapped_addr(u64 phys_addr) +{ + struct efi_memory_map *map; + void *p; + map = efi.memmap; + if (!map) + return NULL; + if (WARN_ON(!map->map)) + return NULL; + for (p = map->map; p < map->map_end; p += map->desc_size) { + efi_memory_desc_t *md = p; + u64 size = md->num_pages << EFI_PAGE_SHIFT; + u64 end = md->phys_addr + size; + if (!(md->attribute & EFI_MEMORY_RUNTIME) && + md->type != EFI_BOOT_SERVICES_CODE && + md->type != EFI_BOOT_SERVICES_DATA) + continue; + if (!md->virt_addr) + continue; + if (phys_addr >= md->phys_addr && phys_addr < end) { + phys_addr += md->virt_addr - md->phys_addr; + return (__force void __iomem *)(unsigned long)phys_addr; + } + } + return NULL; +} + static __initdata efi_config_table_type_t common_tables[] = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, diff --git a/include/linux/efi.h b/include/linux/efi.h index 09d9e4212799..c084b6d942c3 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -558,6 +558,7 @@ extern struct efi { efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; efi_set_virtual_address_map_t *set_virtual_address_map; + struct efi_memory_map *memmap; } efi; static inline int From 4172fe2f8a479e2237459918edc83b027efa8808 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:25 -0700 Subject: [PATCH 05/26] EFI stub documentation updates Move efi-stub.txt out of x86 directory and into common directory in preparation for adding ARM EFI stub support. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- Documentation/{x86 => }/efi-stub.txt | 0 arch/x86/Kconfig | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Documentation/{x86 => }/efi-stub.txt (100%) diff --git a/Documentation/x86/efi-stub.txt b/Documentation/efi-stub.txt similarity index 100% rename from Documentation/x86/efi-stub.txt rename to Documentation/efi-stub.txt diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b32ebf92b0ce..ec65b51af959 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1579,7 +1579,7 @@ config EFI_STUB This kernel feature allows a bzImage to be loaded directly by EFI firmware without the use of a bootloader. - See Documentation/x86/efi-stub.txt for more information. + See Documentation/efi-stub.txt for more information. config SECCOMP def_bool y From ed37ddffe201bfad7be3c45bc08bd65b5298adca Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:26 -0700 Subject: [PATCH 06/26] efi: Add proper definitions for some EFI function pointers. The x86/AMD64 EFI stubs must use a call wrapper to convert between the Linux and EFI ABIs, so void pointers are sufficient. For ARM, the ABIs are compatible, so we can directly invoke the function pointers. The functions that are used by the ARM stub are updated to match the EFI definitions. Also add some EFI types used by EFI functions. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.h | 8 ----- include/linux/efi.h | 50 ++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h index e5b0a8f91c5f..02265107cce3 100644 --- a/arch/x86/boot/compressed/eboot.h +++ b/arch/x86/boot/compressed/eboot.h @@ -10,8 +10,6 @@ #define SEG_GRANULARITY_4KB (1 << 0) #define DESC_TYPE_CODE_DATA (1 << 0) - -#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) #define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_CONSOLE_OUT_DEVICE_GUID \ @@ -62,10 +60,4 @@ struct efi_uga_draw_protocol { void *blt; }; -struct efi_simple_text_output_protocol { - void *reset; - void *output_string; - void *test_string; -}; - #endif /* BOOT_COMPRESSED_EBOOT_H */ diff --git a/include/linux/efi.h b/include/linux/efi.h index c084b6d942c3..bc5687d0f315 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -39,6 +39,8 @@ typedef unsigned long efi_status_t; typedef u8 efi_bool_t; typedef u16 efi_char16_t; /* UNICODE character */ +typedef u64 efi_physical_addr_t; +typedef void *efi_handle_t; typedef struct { @@ -96,6 +98,7 @@ typedef struct { #define EFI_MEMORY_DESCRIPTOR_VERSION 1 #define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT) typedef struct { u32 type; @@ -157,11 +160,13 @@ typedef struct { efi_table_hdr_t hdr; void *raise_tpl; void *restore_tpl; - void *allocate_pages; - void *free_pages; - void *get_memory_map; - void *allocate_pool; - void *free_pool; + efi_status_t (*allocate_pages)(int, int, unsigned long, + efi_physical_addr_t *); + efi_status_t (*free_pages)(efi_physical_addr_t, unsigned long); + efi_status_t (*get_memory_map)(unsigned long *, void *, unsigned long *, + unsigned long *, u32 *); + efi_status_t (*allocate_pool)(int, unsigned long, void **); + efi_status_t (*free_pool)(void *); void *create_event; void *set_timer; void *wait_for_event; @@ -171,7 +176,7 @@ typedef struct { void *install_protocol_interface; void *reinstall_protocol_interface; void *uninstall_protocol_interface; - void *handle_protocol; + efi_status_t (*handle_protocol)(efi_handle_t, efi_guid_t *, void **); void *__reserved; void *register_protocol_notify; void *locate_handle; @@ -181,7 +186,7 @@ typedef struct { void *start_image; void *exit; void *unload_image; - void *exit_boot_services; + efi_status_t (*exit_boot_services)(efi_handle_t, unsigned long); void *get_next_monotonic_count; void *stall; void *set_watchdog_timer; @@ -494,10 +499,6 @@ typedef struct { unsigned long unload; } efi_loaded_image_t; -typedef struct { - u64 revision; - void *open_volume; -} efi_file_io_interface_t; typedef struct { u64 size; @@ -510,20 +511,30 @@ typedef struct { efi_char16_t filename[1]; } efi_file_info_t; -typedef struct { +typedef struct _efi_file_handle { u64 revision; - void *open; - void *close; + efi_status_t (*open)(struct _efi_file_handle *, + struct _efi_file_handle **, + efi_char16_t *, u64, u64); + efi_status_t (*close)(struct _efi_file_handle *); void *delete; - void *read; + efi_status_t (*read)(struct _efi_file_handle *, unsigned long *, + void *); void *write; void *get_position; void *set_position; - void *get_info; + efi_status_t (*get_info)(struct _efi_file_handle *, efi_guid_t *, + unsigned long *, void *); void *set_info; void *flush; } efi_file_handle_t; +typedef struct _efi_file_io_interface { + u64 revision; + int (*open_volume)(struct _efi_file_io_interface *, + efi_file_handle_t **); +} efi_file_io_interface_t; + #define EFI_FILE_MODE_READ 0x0000000000000001 #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 @@ -792,6 +803,13 @@ struct efivar_entry { struct kobject kobj; }; + +struct efi_simple_text_output_protocol { + void *reset; + efi_status_t (*output_string)(void *, void *); + void *test_string; +}; + extern struct list_head efivar_sysfs_list; static inline void From 7721da4c1ebf96ff815987011c1a0edef596b50a Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:27 -0700 Subject: [PATCH 07/26] efi: Move common EFI stub code from x86 arch code to common location No code changes made, just moving functions and #define from x86 arch directory to common location. Code is shared using #include, similar to how decompression code is shared among architectures. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 442 +---------------------- arch/x86/boot/compressed/eboot.h | 1 - drivers/firmware/efi/efi-stub-helper.c | 463 +++++++++++++++++++++++++ 3 files changed, 464 insertions(+), 442 deletions(-) create mode 100644 drivers/firmware/efi/efi-stub-helper.c diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index b7388a425f09..ab0eefcc5123 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -19,214 +19,10 @@ static efi_system_table_t *sys_table; -static void efi_char16_printk(efi_char16_t *str) -{ - struct efi_simple_text_output_protocol *out; - out = (struct efi_simple_text_output_protocol *)sys_table->con_out; - efi_call_phys2(out->output_string, out, str); -} +#include "../../../../drivers/firmware/efi/efi-stub-helper.c" -static void efi_printk(char *str) -{ - char *s8; - for (s8 = str; *s8; s8++) { - efi_char16_t ch[2] = { 0 }; - - ch[0] = *s8; - if (*s8 == '\n') { - efi_char16_t nl[2] = { '\r', 0 }; - efi_char16_printk(nl); - } - - efi_char16_printk(ch); - } -} - -static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, - unsigned long *desc_size) -{ - efi_memory_desc_t *m = NULL; - efi_status_t status; - unsigned long key; - u32 desc_version; - - *map_size = sizeof(*m) * 32; -again: - /* - * Add an additional efi_memory_desc_t because we're doing an - * allocation which may be in a new descriptor region. - */ - *map_size += sizeof(*m); - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, *map_size, (void **)&m); - if (status != EFI_SUCCESS) - goto fail; - - status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, - m, &key, desc_size, &desc_version); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, m); - goto again; - } - - if (status != EFI_SUCCESS) - efi_call_phys1(sys_table->boottime->free_pool, m); - -fail: - *map = m; - return status; -} - -/* - * Allocate at the highest possible address that is not above 'max'. - */ -static efi_status_t high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) -{ - unsigned long map_size, desc_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - u64 max_addr = 0; - int i; - - status = __get_map(&map, &map_size, &desc_size); - if (status != EFI_SUCCESS) - goto fail; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; -again: - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = (efi_memory_desc_t *)(m + (i * desc_size)); - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); - - if ((start + size) > end || (start + size) > max) - continue; - - if (end - size > max) - end = max; - - if (round_down(end - size, align) < start) - continue; - - start = round_down(end - size, align); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. - */ - if (start == 0x0) - continue; - - if (start > max_addr) - max_addr = start; - } - - if (!max_addr) - status = EFI_NOT_FOUND; - else { - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &max_addr); - if (status != EFI_SUCCESS) { - max = max_addr; - max_addr = 0; - goto again; - } - - *addr = max_addr; - } - -free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); - -fail: - return status; -} - -/* - * Allocate at the lowest possible address. - */ -static efi_status_t low_alloc(unsigned long size, unsigned long align, - unsigned long *addr) -{ - unsigned long map_size, desc_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - int i; - - status = __get_map(&map, &map_size, &desc_size); - if (status != EFI_SUCCESS) - goto fail; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = (efi_memory_desc_t *)(m + (i * desc_size)); - - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); - - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. Skip the first 8 - * bytes so we start at a nice even number. - */ - if (start == 0x0) - start += 8; - - start = round_up(start, align); - if ((start + size) > end) - continue; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); - if (status == EFI_SUCCESS) { - *addr = start; - break; - } - } - - if (i == map_size / desc_size) - status = EFI_NOT_FOUND; - -free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); -fail: - return status; -} - -static void low_free(unsigned long size, unsigned long addr) -{ - unsigned long nr_pages; - - nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); -} static void find_bits(unsigned long mask, u8 *pos, u8 *size) { @@ -624,242 +420,6 @@ void setup_graphics(struct boot_params *boot_params) } } -struct initrd { - efi_file_handle_t *handle; - u64 size; -}; - -/* - * Check the cmdline for a LILO-style initrd= arguments. - * - * We only support loading an initrd from the same filesystem as the - * kernel image. - */ -static efi_status_t handle_ramdisks(efi_loaded_image_t *image, - struct setup_header *hdr) -{ - struct initrd *initrds; - unsigned long initrd_addr; - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; - u64 initrd_total; - efi_file_io_interface_t *io; - efi_file_handle_t *fh; - efi_status_t status; - int nr_initrds; - char *str; - int i, j, k; - - initrd_addr = 0; - initrd_total = 0; - - str = (char *)(unsigned long)hdr->cmd_line_ptr; - - j = 0; /* See close_handles */ - - if (!str || !*str) - return EFI_SUCCESS; - - for (nr_initrds = 0; *str; nr_initrds++) { - str = strstr(str, "initrd="); - if (!str) - break; - - str += 7; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') - str++; - } - - if (!nr_initrds) - return EFI_SUCCESS; - - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, - nr_initrds * sizeof(*initrds), - &initrds); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrds\n"); - goto fail; - } - - str = (char *)(unsigned long)hdr->cmd_line_ptr; - for (i = 0; i < nr_initrds; i++) { - struct initrd *initrd; - efi_file_handle_t *h; - efi_file_info_t *info; - efi_char16_t filename_16[256]; - unsigned long info_sz; - efi_guid_t info_guid = EFI_FILE_INFO_ID; - efi_char16_t *p; - u64 file_sz; - - str = strstr(str, "initrd="); - if (!str) - break; - - str += 7; - - initrd = &initrds[i]; - p = filename_16; - - /* Skip any leading slashes */ - while (*str == '/' || *str == '\\') - str++; - - while (*str && *str != ' ' && *str != '\n') { - if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) - break; - - if (*str == '/') { - *p++ = '\\'; - *str++; - } else { - *p++ = *str++; - } - } - - *p = '\0'; - - /* Only open the volume once. */ - if (!i) { - efi_boot_services_t *boottime; - - boottime = sys_table->boottime; - - status = efi_call_phys3(boottime->handle_protocol, - image->device_handle, &fs_proto, &io); - if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); - goto free_initrds; - } - - status = efi_call_phys2(io->open_volume, io, &fh); - if (status != EFI_SUCCESS) { - efi_printk("Failed to open volume\n"); - goto free_initrds; - } - } - - status = efi_call_phys5(fh->open, fh, &h, filename_16, - EFI_FILE_MODE_READ, (u64)0); - if (status != EFI_SUCCESS) { - efi_printk("Failed to open initrd file: "); - efi_char16_printk(filename_16); - efi_printk("\n"); - goto close_handles; - } - - initrd->handle = h; - - info_sz = 0; - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get initrd info size\n"); - goto close_handles; - } - -grow: - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, info_sz, &info); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrd info\n"); - goto close_handles; - } - - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, info); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, info); - goto grow; - } - - file_sz = info->file_size; - efi_call_phys1(sys_table->boottime->free_pool, info); - - if (status != EFI_SUCCESS) { - efi_printk("Failed to get initrd info\n"); - goto close_handles; - } - - initrd->size = file_sz; - initrd_total += file_sz; - } - - if (initrd_total) { - unsigned long addr; - - /* - * Multiple initrd's need to be at consecutive - * addresses in memory, so allocate enough memory for - * all the initrd's. - */ - status = high_alloc(initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); - if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc highmem for initrds\n"); - goto close_handles; - } - - /* We've run out of free low memory. */ - if (initrd_addr > hdr->initrd_addr_max) { - efi_printk("We've run out of free low memory\n"); - status = EFI_INVALID_PARAMETER; - goto free_initrd_total; - } - - addr = initrd_addr; - for (j = 0; j < nr_initrds; j++) { - u64 size; - - size = initrds[j].size; - while (size) { - u64 chunksize; - if (size > EFI_READ_CHUNK_SIZE) - chunksize = EFI_READ_CHUNK_SIZE; - else - chunksize = size; - status = efi_call_phys3(fh->read, - initrds[j].handle, - &chunksize, addr); - if (status != EFI_SUCCESS) { - efi_printk("Failed to read initrd\n"); - goto free_initrd_total; - } - addr += chunksize; - size -= chunksize; - } - - efi_call_phys1(fh->close, initrds[j].handle); - } - - } - - efi_call_phys1(sys_table->boottime->free_pool, initrds); - - hdr->ramdisk_image = initrd_addr; - hdr->ramdisk_size = initrd_total; - - return status; - -free_initrd_total: - low_free(initrd_total, initrd_addr); - -close_handles: - for (k = j; k < i; k++) - efi_call_phys1(fh->close, initrds[k].handle); -free_initrds: - efi_call_phys1(sys_table->boottime->free_pool, initrds); -fail: - hdr->ramdisk_image = 0; - hdr->ramdisk_size = 0; - - return status; -} /* * Because the x86 boot code expects to be passed a boot_params we diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h index 02265107cce3..81b6b652b46a 100644 --- a/arch/x86/boot/compressed/eboot.h +++ b/arch/x86/boot/compressed/eboot.h @@ -10,7 +10,6 @@ #define SEG_GRANULARITY_4KB (1 << 0) #define DESC_TYPE_CODE_DATA (1 << 0) -#define EFI_READ_CHUNK_SIZE (1024 * 1024) #define EFI_CONSOLE_OUT_DEVICE_GUID \ EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \ diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c new file mode 100644 index 000000000000..8a83387fef92 --- /dev/null +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -0,0 +1,463 @@ +/* + * Helper functions used by the EFI stub on multiple + * architectures. This should be #included by the EFI stub + * implementation files. + * + * Copyright 2011 Intel Corporation; author Matt Fleming + * + * This file is part of the Linux kernel, and is made available + * under the terms of the GNU General Public License version 2. + * + */ +#define EFI_READ_CHUNK_SIZE (1024 * 1024) + +struct initrd { + efi_file_handle_t *handle; + u64 size; +}; + + + + +static void efi_char16_printk(efi_char16_t *str) +{ + struct efi_simple_text_output_protocol *out; + + out = (struct efi_simple_text_output_protocol *)sys_table->con_out; + efi_call_phys2(out->output_string, out, str); +} + +static void efi_printk(char *str) +{ + char *s8; + + for (s8 = str; *s8; s8++) { + efi_char16_t ch[2] = { 0 }; + + ch[0] = *s8; + if (*s8 == '\n') { + efi_char16_t nl[2] = { '\r', 0 }; + efi_char16_printk(nl); + } + + efi_char16_printk(ch); + } +} + + +static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, + unsigned long *desc_size) +{ + efi_memory_desc_t *m = NULL; + efi_status_t status; + unsigned long key; + u32 desc_version; + + *map_size = sizeof(*m) * 32; +again: + /* + * Add an additional efi_memory_desc_t because we're doing an + * allocation which may be in a new descriptor region. + */ + *map_size += sizeof(*m); + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, *map_size, (void **)&m); + if (status != EFI_SUCCESS) + goto fail; + + status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, + m, &key, desc_size, &desc_version); + if (status == EFI_BUFFER_TOO_SMALL) { + efi_call_phys1(sys_table->boottime->free_pool, m); + goto again; + } + + if (status != EFI_SUCCESS) + efi_call_phys1(sys_table->boottime->free_pool, m); + +fail: + *map = m; + return status; +} + +/* + * Allocate at the highest possible address that is not above 'max'. + */ +static efi_status_t high_alloc(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) +{ + unsigned long map_size, desc_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + u64 max_addr = 0; + int i; + + status = __get_map(&map, &map_size, &desc_size); + if (status != EFI_SUCCESS) + goto fail; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; +again: + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = (efi_memory_desc_t *)(m + (i * desc_size)); + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); + + if ((start + size) > end || (start + size) > max) + continue; + + if (end - size > max) + end = max; + + if (round_down(end - size, align) < start) + continue; + + start = round_down(end - size, align); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. + */ + if (start == 0x0) + continue; + + if (start > max_addr) + max_addr = start; + } + + if (!max_addr) + status = EFI_NOT_FOUND; + else { + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &max_addr); + if (status != EFI_SUCCESS) { + max = max_addr; + max_addr = 0; + goto again; + } + + *addr = max_addr; + } + +free_pool: + efi_call_phys1(sys_table->boottime->free_pool, map); + +fail: + return status; +} + +/* + * Allocate at the lowest possible address. + */ +static efi_status_t low_alloc(unsigned long size, unsigned long align, + unsigned long *addr) +{ + unsigned long map_size, desc_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + int i; + + status = __get_map(&map, &map_size, &desc_size); + if (status != EFI_SUCCESS) + goto fail; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + for (i = 0; i < map_size / desc_size; i++) { + efi_memory_desc_t *desc; + unsigned long m = (unsigned long)map; + u64 start, end; + + desc = (efi_memory_desc_t *)(m + (i * desc_size)); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); + + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. Skip the first 8 + * bytes so we start at a nice even number. + */ + if (start == 0x0) + start += 8; + + start = round_up(start, align); + if ((start + size) > end) + continue; + + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &start); + if (status == EFI_SUCCESS) { + *addr = start; + break; + } + } + + if (i == map_size / desc_size) + status = EFI_NOT_FOUND; + +free_pool: + efi_call_phys1(sys_table->boottime->free_pool, map); +fail: + return status; +} + +static void low_free(unsigned long size, unsigned long addr) +{ + unsigned long nr_pages; + + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); +} + + +/* + * Check the cmdline for a LILO-style initrd= arguments. + * + * We only support loading an initrd from the same filesystem as the + * kernel image. + */ +static efi_status_t handle_ramdisks(efi_loaded_image_t *image, + struct setup_header *hdr) +{ + struct initrd *initrds; + unsigned long initrd_addr; + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + u64 initrd_total; + efi_file_io_interface_t *io; + efi_file_handle_t *fh; + efi_status_t status; + int nr_initrds; + char *str; + int i, j, k; + + initrd_addr = 0; + initrd_total = 0; + + str = (char *)(unsigned long)hdr->cmd_line_ptr; + + j = 0; /* See close_handles */ + + if (!str || !*str) + return EFI_SUCCESS; + + for (nr_initrds = 0; *str; nr_initrds++) { + str = strstr(str, "initrd="); + if (!str) + break; + + str += 7; + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') + str++; + } + + if (!nr_initrds) + return EFI_SUCCESS; + + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, + nr_initrds * sizeof(*initrds), + &initrds); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc mem for initrds\n"); + goto fail; + } + + str = (char *)(unsigned long)hdr->cmd_line_ptr; + for (i = 0; i < nr_initrds; i++) { + struct initrd *initrd; + efi_file_handle_t *h; + efi_file_info_t *info; + efi_char16_t filename_16[256]; + unsigned long info_sz; + efi_guid_t info_guid = EFI_FILE_INFO_ID; + efi_char16_t *p; + u64 file_sz; + + str = strstr(str, "initrd="); + if (!str) + break; + + str += 7; + + initrd = &initrds[i]; + p = filename_16; + + /* Skip any leading slashes */ + while (*str == '/' || *str == '\\') + str++; + + while (*str && *str != ' ' && *str != '\n') { + if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) + break; + + if (*str == '/') { + *p++ = '\\'; + *str++; + } else { + *p++ = *str++; + } + } + + *p = '\0'; + + /* Only open the volume once. */ + if (!i) { + efi_boot_services_t *boottime; + + boottime = sys_table->boottime; + + status = efi_call_phys3(boottime->handle_protocol, + image->device_handle, &fs_proto, &io); + if (status != EFI_SUCCESS) { + efi_printk("Failed to handle fs_proto\n"); + goto free_initrds; + } + + status = efi_call_phys2(io->open_volume, io, &fh); + if (status != EFI_SUCCESS) { + efi_printk("Failed to open volume\n"); + goto free_initrds; + } + } + + status = efi_call_phys5(fh->open, fh, &h, filename_16, + EFI_FILE_MODE_READ, (u64)0); + if (status != EFI_SUCCESS) { + efi_printk("Failed to open initrd file: "); + efi_char16_printk(filename_16); + efi_printk("\n"); + goto close_handles; + } + + initrd->handle = h; + + info_sz = 0; + status = efi_call_phys4(h->get_info, h, &info_guid, + &info_sz, NULL); + if (status != EFI_BUFFER_TOO_SMALL) { + efi_printk("Failed to get initrd info size\n"); + goto close_handles; + } + +grow: + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, info_sz, &info); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc mem for initrd info\n"); + goto close_handles; + } + + status = efi_call_phys4(h->get_info, h, &info_guid, + &info_sz, info); + if (status == EFI_BUFFER_TOO_SMALL) { + efi_call_phys1(sys_table->boottime->free_pool, info); + goto grow; + } + + file_sz = info->file_size; + efi_call_phys1(sys_table->boottime->free_pool, info); + + if (status != EFI_SUCCESS) { + efi_printk("Failed to get initrd info\n"); + goto close_handles; + } + + initrd->size = file_sz; + initrd_total += file_sz; + } + + if (initrd_total) { + unsigned long addr; + + /* + * Multiple initrd's need to be at consecutive + * addresses in memory, so allocate enough memory for + * all the initrd's. + */ + status = high_alloc(initrd_total, 0x1000, + &initrd_addr, hdr->initrd_addr_max); + if (status != EFI_SUCCESS) { + efi_printk("Failed to alloc highmem for initrds\n"); + goto close_handles; + } + + /* We've run out of free low memory. */ + if (initrd_addr > hdr->initrd_addr_max) { + efi_printk("We've run out of free low memory\n"); + status = EFI_INVALID_PARAMETER; + goto free_initrd_total; + } + + addr = initrd_addr; + for (j = 0; j < nr_initrds; j++) { + u64 size; + + size = initrds[j].size; + while (size) { + u64 chunksize; + if (size > EFI_READ_CHUNK_SIZE) + chunksize = EFI_READ_CHUNK_SIZE; + else + chunksize = size; + status = efi_call_phys3(fh->read, + initrds[j].handle, + &chunksize, addr); + if (status != EFI_SUCCESS) { + efi_printk("Failed to read initrd\n"); + goto free_initrd_total; + } + addr += chunksize; + size -= chunksize; + } + + efi_call_phys1(fh->close, initrds[j].handle); + } + + } + + efi_call_phys1(sys_table->boottime->free_pool, initrds); + + hdr->ramdisk_image = initrd_addr; + hdr->ramdisk_size = initrd_total; + + return status; + +free_initrd_total: + low_free(initrd_total, initrd_addr); + +close_handles: + for (k = j; k < i; k++) + efi_call_phys1(fh->close, initrds[k].handle); +free_initrds: + efi_call_phys1(sys_table->boottime->free_pool, initrds); +fail: + hdr->ramdisk_image = 0; + hdr->ramdisk_size = 0; + + return status; +} From 876dc36aceb1682cb01426f507f369aec4f68c76 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:28 -0700 Subject: [PATCH 08/26] efi: Add system table pointer argument to shared functions. Add system table pointer argument to shared EFI stub related functions so they no longer use a global system table pointer as they did when part of eboot.c. For the ARM EFI stub this allows us to avoid global variables completely and thereby not have to deal with GOT fixups. Not having the EFI stub fixup its GOT, which is shared with the decompressor, simplifies the relocating of the zImage to a bootable address. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 38 +++++----- drivers/firmware/efi/efi-stub-helper.c | 96 ++++++++++++++------------ 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ab0eefcc5123..65b6a34f56d6 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -453,13 +453,13 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) status = efi_call_phys3(sys_table->boottime->handle_protocol, handle, &proto, (void *)&image); if (status != EFI_SUCCESS) { - efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); + efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); return NULL; } - status = low_alloc(0x4000, 1, (unsigned long *)&boot_params); + status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc lowmem for boot params\n"); + efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); return NULL; } @@ -503,9 +503,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) options_size++; /* NUL termination */ - status = low_alloc(options_size, 1, &cmdline); + status = low_alloc(sys_table, options_size, 1, + &cmdline); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for cmdline\n"); + efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); goto fail; } @@ -529,16 +530,16 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) memset(sdt, 0, sizeof(*sdt)); - status = handle_ramdisks(image, hdr); + status = handle_ramdisks(sys_table, image, hdr); if (status != EFI_SUCCESS) goto fail2; return boot_params; fail2: if (options_size) - low_free(options_size, hdr->cmd_line_ptr); + low_free(sys_table, options_size, hdr->cmd_line_ptr); fail: - low_free(0x4000, (unsigned long)boot_params); + low_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; } @@ -561,7 +562,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, again: size += sizeof(*mem_map) * 2; _size = size; - status = low_alloc(size, 1, (unsigned long *)&mem_map); + status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); if (status != EFI_SUCCESS) return status; @@ -569,7 +570,7 @@ get_map: status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, mem_map, &key, &desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - low_free(_size, (unsigned long)mem_map); + low_free(sys_table, _size, (unsigned long)mem_map); goto again; } @@ -671,7 +672,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - low_free(_size, (unsigned long)mem_map); + low_free(sys_table, _size, (unsigned long)mem_map); return status; } @@ -694,10 +695,10 @@ static efi_status_t relocate_kernel(struct setup_header *hdr) EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status != EFI_SUCCESS) { - status = low_alloc(hdr->init_size, hdr->kernel_alignment, - &start); + status = low_alloc(sys_table, hdr->init_size, + hdr->kernel_alignment, &start); if (status != EFI_SUCCESS) - efi_printk("Failed to alloc mem for kernel\n"); + efi_printk(sys_table, "Failed to alloc mem for kernel\n"); } if (status == EFI_SUCCESS) @@ -737,14 +738,15 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, EFI_LOADER_DATA, sizeof(*gdt), (void **)&gdt); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for gdt structure\n"); + efi_printk(sys_table, "Failed to alloc mem for gdt structure\n"); goto fail; } gdt->size = 0x800; - status = low_alloc(gdt->size, 8, (unsigned long *)&gdt->address); + status = low_alloc(sys_table, gdt->size, 8, + (unsigned long *)&gdt->address); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for gdt\n"); + efi_printk(sys_table, "Failed to alloc mem for gdt\n"); goto fail; } @@ -752,7 +754,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, EFI_LOADER_DATA, sizeof(*idt), (void **)&idt); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for idt structure\n"); + efi_printk(sys_table, "Failed to alloc mem for idt structure\n"); goto fail; } diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 8a83387fef92..05c539e9a3c8 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -19,15 +19,16 @@ struct initrd { -static void efi_char16_printk(efi_char16_t *str) +static void efi_char16_printk(efi_system_table_t *sys_table_arg, + efi_char16_t *str) { struct efi_simple_text_output_protocol *out; - out = (struct efi_simple_text_output_protocol *)sys_table->con_out; + out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out; efi_call_phys2(out->output_string, out, str); } -static void efi_printk(char *str) +static void efi_printk(efi_system_table_t *sys_table_arg, char *str) { char *s8; @@ -37,15 +38,17 @@ static void efi_printk(char *str) ch[0] = *s8; if (*s8 == '\n') { efi_char16_t nl[2] = { '\r', 0 }; - efi_char16_printk(nl); + efi_char16_printk(sys_table_arg, nl); } - efi_char16_printk(ch); + efi_char16_printk(sys_table_arg, ch); } } -static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, +static efi_status_t __get_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, unsigned long *desc_size) { efi_memory_desc_t *m = NULL; @@ -60,20 +63,20 @@ again: * allocation which may be in a new descriptor region. */ *map_size += sizeof(*m); - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, *map_size, (void **)&m); if (status != EFI_SUCCESS) goto fail; - status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, - m, &key, desc_size, &desc_version); + status = efi_call_phys5(sys_table_arg->boottime->get_memory_map, + map_size, m, &key, desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, m); + efi_call_phys1(sys_table_arg->boottime->free_pool, m); goto again; } if (status != EFI_SUCCESS) - efi_call_phys1(sys_table->boottime->free_pool, m); + efi_call_phys1(sys_table_arg->boottime->free_pool, m); fail: *map = m; @@ -83,8 +86,9 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -static efi_status_t high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) +static efi_status_t high_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr, unsigned long max) { unsigned long map_size, desc_size; efi_memory_desc_t *map; @@ -93,7 +97,7 @@ static efi_status_t high_alloc(unsigned long size, unsigned long align, u64 max_addr = 0; int i; - status = __get_map(&map, &map_size, &desc_size); + status = __get_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -139,7 +143,7 @@ again: if (!max_addr) status = EFI_NOT_FOUND; else { - status = efi_call_phys4(sys_table->boottime->allocate_pages, + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &max_addr); if (status != EFI_SUCCESS) { @@ -152,7 +156,7 @@ again: } free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); + efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; @@ -161,7 +165,8 @@ fail: /* * Allocate at the lowest possible address. */ -static efi_status_t low_alloc(unsigned long size, unsigned long align, +static efi_status_t low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, unsigned long *addr) { unsigned long map_size, desc_size; @@ -170,7 +175,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, unsigned long nr_pages; int i; - status = __get_map(&map, &map_size, &desc_size); + status = __get_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -203,7 +208,7 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, if ((start + size) > end) continue; - status = efi_call_phys4(sys_table->boottime->allocate_pages, + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status == EFI_SUCCESS) { @@ -216,17 +221,18 @@ static efi_status_t low_alloc(unsigned long size, unsigned long align, status = EFI_NOT_FOUND; free_pool: - efi_call_phys1(sys_table->boottime->free_pool, map); + efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; } -static void low_free(unsigned long size, unsigned long addr) +static void low_free(efi_system_table_t *sys_table_arg, unsigned long size, + unsigned long addr) { unsigned long nr_pages; nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); + efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages); } @@ -236,7 +242,8 @@ static void low_free(unsigned long size, unsigned long addr) * We only support loading an initrd from the same filesystem as the * kernel image. */ -static efi_status_t handle_ramdisks(efi_loaded_image_t *image, +static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, struct setup_header *hdr) { struct initrd *initrds; @@ -278,12 +285,12 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, if (!nr_initrds) return EFI_SUCCESS; - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, nr_initrds * sizeof(*initrds), &initrds); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n"); goto fail; } @@ -329,18 +336,18 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, if (!i) { efi_boot_services_t *boottime; - boottime = sys_table->boottime; + boottime = sys_table_arg->boottime; status = efi_call_phys3(boottime->handle_protocol, image->device_handle, &fs_proto, &io); if (status != EFI_SUCCESS) { - efi_printk("Failed to handle fs_proto\n"); + efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); goto free_initrds; } status = efi_call_phys2(io->open_volume, io, &fh); if (status != EFI_SUCCESS) { - efi_printk("Failed to open volume\n"); + efi_printk(sys_table_arg, "Failed to open volume\n"); goto free_initrds; } } @@ -348,9 +355,9 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, status = efi_call_phys5(fh->open, fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); if (status != EFI_SUCCESS) { - efi_printk("Failed to open initrd file: "); - efi_char16_printk(filename_16); - efi_printk("\n"); + efi_printk(sys_table_arg, "Failed to open initrd file: "); + efi_char16_printk(sys_table_arg, filename_16); + efi_printk(sys_table_arg, "\n"); goto close_handles; } @@ -360,30 +367,31 @@ static efi_status_t handle_ramdisks(efi_loaded_image_t *image, status = efi_call_phys4(h->get_info, h, &info_guid, &info_sz, NULL); if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk("Failed to get initrd info size\n"); + efi_printk(sys_table_arg, "Failed to get initrd info size\n"); goto close_handles; } grow: - status = efi_call_phys3(sys_table->boottime->allocate_pool, + status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, info_sz, &info); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc mem for initrd info\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n"); goto close_handles; } status = efi_call_phys4(h->get_info, h, &info_guid, &info_sz, info); if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table->boottime->free_pool, info); + efi_call_phys1(sys_table_arg->boottime->free_pool, + info); goto grow; } file_sz = info->file_size; - efi_call_phys1(sys_table->boottime->free_pool, info); + efi_call_phys1(sys_table_arg->boottime->free_pool, info); if (status != EFI_SUCCESS) { - efi_printk("Failed to get initrd info\n"); + efi_printk(sys_table_arg, "Failed to get initrd info\n"); goto close_handles; } @@ -399,16 +407,16 @@ grow: * addresses in memory, so allocate enough memory for * all the initrd's. */ - status = high_alloc(initrd_total, 0x1000, + status = high_alloc(sys_table_arg, initrd_total, 0x1000, &initrd_addr, hdr->initrd_addr_max); if (status != EFI_SUCCESS) { - efi_printk("Failed to alloc highmem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; } /* We've run out of free low memory. */ if (initrd_addr > hdr->initrd_addr_max) { - efi_printk("We've run out of free low memory\n"); + efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; goto free_initrd_total; } @@ -428,7 +436,7 @@ grow: initrds[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { - efi_printk("Failed to read initrd\n"); + efi_printk(sys_table_arg, "Failed to read initrd\n"); goto free_initrd_total; } addr += chunksize; @@ -440,7 +448,7 @@ grow: } - efi_call_phys1(sys_table->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); hdr->ramdisk_image = initrd_addr; hdr->ramdisk_size = initrd_total; @@ -448,13 +456,13 @@ grow: return status; free_initrd_total: - low_free(initrd_total, initrd_addr); + low_free(sys_table_arg, initrd_total, initrd_addr); close_handles: for (k = j; k < i; k++) efi_call_phys1(fh->close, initrds[k].handle); free_initrds: - efi_call_phys1(sys_table->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); fail: hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; From 40e4530a00b7d52332fe9a1361388fda8e5260da Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:29 -0700 Subject: [PATCH 09/26] efi: Rename memory allocation/free functions Rename them to be more similar, as low_free() could be used to free memory allocated by both high_alloc() and low_alloc(). high_alloc() -> efi_high_alloc() low_alloc() -> efi_low_alloc() low_free() -> efi_free() Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 19 ++++++++++--------- drivers/firmware/efi/efi-stub-helper.c | 14 +++++++------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 65b6a34f56d6..2a4430a0fb03 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -457,7 +457,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return NULL; } - status = low_alloc(sys_table, 0x4000, 1, (unsigned long *)&boot_params); + status = efi_low_alloc(sys_table, 0x4000, 1, + (unsigned long *)&boot_params); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); return NULL; @@ -503,7 +504,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) options_size++; /* NUL termination */ - status = low_alloc(sys_table, options_size, 1, + status = efi_low_alloc(sys_table, options_size, 1, &cmdline); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); @@ -537,9 +538,9 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return boot_params; fail2: if (options_size) - low_free(sys_table, options_size, hdr->cmd_line_ptr); + efi_free(sys_table, options_size, hdr->cmd_line_ptr); fail: - low_free(sys_table, 0x4000, (unsigned long)boot_params); + efi_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; } @@ -562,7 +563,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, again: size += sizeof(*mem_map) * 2; _size = size; - status = low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); + status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); if (status != EFI_SUCCESS) return status; @@ -570,7 +571,7 @@ get_map: status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, mem_map, &key, &desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { - low_free(sys_table, _size, (unsigned long)mem_map); + efi_free(sys_table, _size, (unsigned long)mem_map); goto again; } @@ -672,7 +673,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - low_free(sys_table, _size, (unsigned long)mem_map); + efi_free(sys_table, _size, (unsigned long)mem_map); return status; } @@ -695,7 +696,7 @@ static efi_status_t relocate_kernel(struct setup_header *hdr) EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status != EFI_SUCCESS) { - status = low_alloc(sys_table, hdr->init_size, + status = efi_low_alloc(sys_table, hdr->init_size, hdr->kernel_alignment, &start); if (status != EFI_SUCCESS) efi_printk(sys_table, "Failed to alloc mem for kernel\n"); @@ -743,7 +744,7 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, } gdt->size = 0x800; - status = low_alloc(sys_table, gdt->size, 8, + status = efi_low_alloc(sys_table, gdt->size, 8, (unsigned long *)&gdt->address); if (status != EFI_SUCCESS) { efi_printk(sys_table, "Failed to alloc mem for gdt\n"); diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 05c539e9a3c8..2f528fb541f9 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -86,7 +86,7 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -static efi_status_t high_alloc(efi_system_table_t *sys_table_arg, +static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, unsigned long size, unsigned long align, unsigned long *addr, unsigned long max) { @@ -165,8 +165,8 @@ fail: /* * Allocate at the lowest possible address. */ -static efi_status_t low_alloc(efi_system_table_t *sys_table_arg, - unsigned long size, unsigned long align, +static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, unsigned long *addr) { unsigned long map_size, desc_size; @@ -226,7 +226,7 @@ fail: return status; } -static void low_free(efi_system_table_t *sys_table_arg, unsigned long size, +static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, unsigned long addr) { unsigned long nr_pages; @@ -407,8 +407,8 @@ grow: * addresses in memory, so allocate enough memory for * all the initrd's. */ - status = high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); + status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, + &initrd_addr, hdr->initrd_addr_max); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; @@ -456,7 +456,7 @@ grow: return status; free_initrd_total: - low_free(sys_table_arg, initrd_total, initrd_addr); + efi_free(sys_table_arg, initrd_total, initrd_addr); close_handles: for (k = j; k < i; k++) From 38dd9c02c3f2ed461165db22b93fae7f3ddde9ac Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:30 -0700 Subject: [PATCH 10/26] efi: Enforce minimum alignment of 1 page on allocations. The efi_high_alloc() and efi_low_alloc() functions use the EFI_ALLOCATE_ADDRESS option to the EFI function allocate_pages(), which requires a minimum of page alignment, and rejects all other requests. The existing code could fail to allocate depending on allocation size, as although repeated allocation attempts were made, none were guaranteed to be page aligned. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 2f528fb541f9..a25675833598 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -101,6 +101,14 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, if (status != EFI_SUCCESS) goto fail; + /* + * Enforce minimum alignment that EFI requires when requesting + * a specific address. We are doing page-based allocations, + * so we must be aligned to a page. + */ + if (align < EFI_PAGE_SIZE) + align = EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; again: for (i = 0; i < map_size / desc_size; i++) { @@ -179,6 +187,14 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if (status != EFI_SUCCESS) goto fail; + /* + * Enforce minimum alignment that EFI requires when requesting + * a specific address. We are doing page-based allocations, + * so we must be aligned to a page. + */ + if (align < EFI_PAGE_SIZE) + align = EFI_PAGE_SIZE; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; for (i = 0; i < map_size / desc_size; i++) { efi_memory_desc_t *desc; From c6866d7238d4f26055de8db9c1d5a700e554b2d4 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:31 -0700 Subject: [PATCH 11/26] efi: Move relocate_kernel() to shared file. The relocate_kernel() function will be generalized and used by all architectures, as they all have similar requirements. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 34 ------------------------- drivers/firmware/efi/efi-stub-helper.c | 35 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 2a4430a0fb03..5bbba868e2bc 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -677,40 +677,6 @@ free_mem_map: return status; } -static efi_status_t relocate_kernel(struct setup_header *hdr) -{ - unsigned long start, nr_pages; - efi_status_t status; - - /* - * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has various restrictions - * on the max physical address it can run at. Attempt to move - * the kernel to boot_params.pref_address, or as low as - * possible. - */ - start = hdr->pref_address; - nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); - if (status != EFI_SUCCESS) { - status = efi_low_alloc(sys_table, hdr->init_size, - hdr->kernel_alignment, &start); - if (status != EFI_SUCCESS) - efi_printk(sys_table, "Failed to alloc mem for kernel\n"); - } - - if (status == EFI_SUCCESS) - memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, - hdr->init_size); - - hdr->pref_address = hdr->code32_start; - hdr->code32_start = (__u32)start; - - return status; -} /* * On success we return a pointer to a boot_params structure, and NULL diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index a25675833598..333ed9c37751 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -485,3 +485,38 @@ fail: return status; } + +static efi_status_t relocate_kernel(struct setup_header *hdr) +{ + unsigned long start, nr_pages; + efi_status_t status; + + /* + * The EFI firmware loader could have placed the kernel image + * anywhere in memory, but the kernel has various restrictions + * on the max physical address it can run at. Attempt to move + * the kernel to boot_params.pref_address, or as low as + * possible. + */ + start = hdr->pref_address; + nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + + status = efi_call_phys4(sys_table->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &start); + if (status != EFI_SUCCESS) { + status = efi_low_alloc(sys_table, hdr->init_size, + hdr->kernel_alignment, &start); + if (status != EFI_SUCCESS) + efi_printk(sys_table, "Failed to alloc mem for kernel\n"); + } + + if (status == EFI_SUCCESS) + memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, + hdr->init_size); + + hdr->pref_address = hdr->code32_start; + hdr->code32_start = (__u32)start; + + return status; +} From 4a9f3a7c336a6b0ffeef2523bef93e67b0921163 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:32 -0700 Subject: [PATCH 12/26] efi: Generalize relocate_kernel() for use by other architectures. Rename relocate_kernel() to efi_relocate_kernel(), and take parameters rather than x86 specific structure. Add max_addr argument as for ARM we have some address constraints that we need to enforce when relocating the kernel. Add alloc_size parameter for use by ARM64 which uses an uncompressed kernel, and needs to allocate space for BSS. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 10 +++- drivers/firmware/efi/efi-stub-helper.c | 76 +++++++++++++++++++------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5bbba868e2bc..2e997b6fbdb1 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -733,10 +733,16 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, * address, relocate it. */ if (hdr->pref_address != hdr->code32_start) { - status = relocate_kernel(hdr); - + unsigned long bzimage_addr = hdr->code32_start; + status = efi_relocate_kernel(sys_table, &bzimage_addr, + hdr->init_size, hdr->init_size, + hdr->pref_address, + hdr->kernel_alignment); if (status != EFI_SUCCESS) goto fail; + + hdr->pref_address = hdr->code32_start; + hdr->code32_start = bzimage_addr; } status = exit_boot(boot_params, handle); diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 333ed9c37751..361e24d8d665 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -485,38 +485,72 @@ fail: return status; } - -static efi_status_t relocate_kernel(struct setup_header *hdr) +/* + * Relocate a kernel image, either compressed or uncompressed. + * In the ARM64 case, all kernel images are currently + * uncompressed, and as such when we relocate it we need to + * allocate additional space for the BSS segment. Any low + * memory that this function should avoid needs to be + * unavailable in the EFI memory map, as if the preferred + * address is not available the lowest available address will + * be used. + */ +static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, + unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment) { - unsigned long start, nr_pages; + unsigned long cur_image_addr; + unsigned long new_addr = 0; efi_status_t status; + unsigned long nr_pages; + efi_physical_addr_t efi_addr = preferred_addr; + + if (!image_addr || !image_size || !alloc_size) + return EFI_INVALID_PARAMETER; + if (alloc_size < image_size) + return EFI_INVALID_PARAMETER; + + cur_image_addr = *image_addr; /* * The EFI firmware loader could have placed the kernel image - * anywhere in memory, but the kernel has various restrictions - * on the max physical address it can run at. Attempt to move - * the kernel to boot_params.pref_address, or as low as + * anywhere in memory, but the kernel has restrictions on the + * max physical address it can run at. Some architectures + * also have a prefered address, so first try to relocate + * to the preferred address. If that fails, allocate as low + * as possible while respecting the required alignment. + */ + nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, + EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as * possible. */ - start = hdr->pref_address; - nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - - status = efi_call_phys4(sys_table->boottime->allocate_pages, - EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, - nr_pages, &start); if (status != EFI_SUCCESS) { - status = efi_low_alloc(sys_table, hdr->init_size, - hdr->kernel_alignment, &start); - if (status != EFI_SUCCESS) - efi_printk(sys_table, "Failed to alloc mem for kernel\n"); + status = efi_low_alloc(sys_table_arg, alloc_size, alignment, + &new_addr); + } + if (status != EFI_SUCCESS) { + efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n"); + return status; } - if (status == EFI_SUCCESS) - memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, - hdr->init_size); + /* + * We know source/dest won't overlap since both memory ranges + * have been allocated by UEFI, so we can safely use memcpy. + */ + memcpy((void *)new_addr, (void *)cur_image_addr, image_size); + /* Zero any extra space we may have allocated for BSS. */ + memset((void *)(new_addr + image_size), alloc_size - image_size, 0); - hdr->pref_address = hdr->code32_start; - hdr->code32_start = (__u32)start; + /* Return the new address of the relocated image. */ + *image_addr = new_addr; return status; } From 5fef3870c572a30f9def07c4ded69dc37a9cf5d9 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:33 -0700 Subject: [PATCH 13/26] efi: Move unicode to ASCII conversion to shared function. Move the open-coded conversion to a shared function for use by all architectures. Change the allocation to prefer a high address for ARM, as this is required to avoid conflicts with reserved regions in low memory. We don't know the specifics of these regions until after we process the command line and device tree. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 43 +++--------------- drivers/firmware/efi/efi-stub-helper.c | 61 ++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 2e997b6fbdb1..5e708c0d466f 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -435,11 +435,10 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) struct efi_info *efi; efi_loaded_image_t *image; void *options; - u32 load_options_size; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; int options_size = 0; efi_status_t status; - unsigned long cmdline; + char *cmdline_ptr; u16 *s2; u8 *s1; int i; @@ -487,41 +486,11 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) hdr->type_of_loader = 0x21; /* Convert unicode cmdline to ascii */ - options = image->load_options; - load_options_size = image->load_options_size / 2; /* ASCII */ - cmdline = 0; - s2 = (u16 *)options; - - if (s2) { - while (*s2 && *s2 != '\n' && options_size < load_options_size) { - s2++; - options_size++; - } - - if (options_size) { - if (options_size > hdr->cmdline_size) - options_size = hdr->cmdline_size; - - options_size++; /* NUL termination */ - - status = efi_low_alloc(sys_table, options_size, 1, - &cmdline); - if (status != EFI_SUCCESS) { - efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); - goto fail; - } - - s1 = (u8 *)(unsigned long)cmdline; - s2 = (u16 *)options; - - for (i = 0; i < options_size - 1; i++) - *s1++ = *s2++; - - *s1 = '\0'; - } - } - - hdr->cmd_line_ptr = cmdline; + cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image, + &options_size); + if (!cmdline_ptr) + goto fail; + hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 361e24d8d665..96665b730c51 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -554,3 +554,64 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, return status; } + +/* + * Convert the unicode UEFI command line to ASCII to pass to kernel. + * 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) +{ + 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; + 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++; + } + } + + if (options_size == 0) { + /* No command line options, so return empty string*/ + options_size = 1; + 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 + if (status != EFI_SUCCESS) + return NULL; + + s1 = (u8 *)cmdline_addr; + s2 = (u16 *)options; + + for (i = 0; i < options_size - 1; i++) + *s1++ = *s2++; + + *s1 = '\0'; + + *cmd_line_len = options_size; + return (char *)cmdline_addr; +} From 86cc653b1955c0a47681d16457f9ee9009f2561a Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:35 -0700 Subject: [PATCH 14/26] efi: Rename __get_map() to efi_get_memory_map() Rename function in preparation for making it more flexible and sharing it. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 96665b730c51..561b9cf7745f 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -46,10 +46,10 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) } -static efi_status_t __get_map(efi_system_table_t *sys_table_arg, - efi_memory_desc_t **map, - unsigned long *map_size, - unsigned long *desc_size) +static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, + efi_memory_desc_t **map, + unsigned long *map_size, + unsigned long *desc_size) { efi_memory_desc_t *m = NULL; efi_status_t status; @@ -97,7 +97,7 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, u64 max_addr = 0; int i; - status = __get_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; @@ -183,7 +183,7 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, unsigned long nr_pages; int i; - status = __get_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); if (status != EFI_SUCCESS) goto fail; From 1c089c65f54b70261fdcb4c238986a979c11fad7 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:36 -0700 Subject: [PATCH 15/26] efi: generalize efi_get_memory_map() Add arguments for returning the descriptor version and also the memory map key. The key is required for calling exit_boot_services(). Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 561b9cf7745f..b314bf720272 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -49,7 +49,9 @@ static void efi_printk(efi_system_table_t *sys_table_arg, char *str) static efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, efi_memory_desc_t **map, unsigned long *map_size, - unsigned long *desc_size) + unsigned long *desc_size, + u32 *desc_ver, + unsigned long *key_ptr) { efi_memory_desc_t *m = NULL; efi_status_t status; @@ -77,6 +79,10 @@ again: if (status != EFI_SUCCESS) efi_call_phys1(sys_table_arg->boottime->free_pool, m); + if (key_ptr && status == EFI_SUCCESS) + *key_ptr = key; + if (desc_ver && status == EFI_SUCCESS) + *desc_ver = desc_version; fail: *map = m; @@ -97,7 +103,8 @@ static efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg, u64 max_addr = 0; int i; - status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, + NULL, NULL); if (status != EFI_SUCCESS) goto fail; @@ -183,7 +190,8 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, unsigned long nr_pages; int i; - status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size); + status = efi_get_memory_map(sys_table_arg, &map, &map_size, &desc_size, + NULL, NULL); if (status != EFI_SUCCESS) goto fail; From ae8e9060a3ecfd22bd5059e39f81547613ae376c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:37 -0700 Subject: [PATCH 16/26] efi: use efi_get_memory_map() to get final map for x86 Replace the open-coded memory map getting with the efi_get_memory_map() that is now general enough to use. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5e708c0d466f..bf2c35d5ec1f 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -527,26 +527,13 @@ static efi_status_t exit_boot(struct boot_params *boot_params, u8 nr_entries; int i; - size = sizeof(*mem_map) * 32; +get_map: + status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, + &desc_version, &key); -again: - size += sizeof(*mem_map) * 2; - _size = size; - status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); if (status != EFI_SUCCESS) return status; -get_map: - status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, - mem_map, &key, &desc_size, &desc_version); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_free(sys_table, _size, (unsigned long)mem_map); - goto again; - } - - if (status != EFI_SUCCESS) - goto free_mem_map; - memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); efi->efi_systab = (unsigned long)sys_table; efi->efi_memdesc_size = desc_size; @@ -574,6 +561,7 @@ get_map: goto free_mem_map; called_exit = true; + efi_call_phys1(sys_table->boottime->free_pool, mem_map); goto get_map; } @@ -642,7 +630,7 @@ get_map: return EFI_SUCCESS; free_mem_map: - efi_free(sys_table, _size, (unsigned long)mem_map); + efi_call_phys1(sys_table->boottime->free_pool, mem_map); return status; } From 0e1cadb05bba2293b4575c8cab275313d181d94f Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:38 -0700 Subject: [PATCH 17/26] efi: Allow efi_free() to be called with size of 0 Make efi_free() safely callable with size of 0, similar to free() being callable with NULL pointers, and do nothing in that case. Remove size checks that this makes redundant. This also avoids some size checks in the ARM EFI stub code that will be added as well. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 3 +-- drivers/firmware/efi/efi-stub-helper.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index bf2c35d5ec1f..ef2181a96d09 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -506,8 +506,7 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) return boot_params; fail2: - if (options_size) - efi_free(sys_table, options_size, hdr->cmd_line_ptr); + efi_free(sys_table, options_size, hdr->cmd_line_ptr); fail: efi_free(sys_table, 0x4000, (unsigned long)boot_params); return NULL; diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index b314bf720272..fda510fa3579 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -255,6 +255,9 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, { unsigned long nr_pages; + if (!size) + return; + nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages); } From 46f4582e7cbc5f30127183812d4da875782518f5 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:39 -0700 Subject: [PATCH 18/26] efi: Generalize handle_ramdisks() and rename to handle_cmdline_files(). The handle_cmdline_files now takes the option to handle as a string, and returns the loaded data through parameters, rather than taking an x86 specific setup_header structure. For ARM, this will be used to load a device tree blob in addition to initrd images. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 9 ++++- drivers/firmware/efi/efi-stub-helper.c | 51 +++++++++++++++----------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ef2181a96d09..beb07a4529ac 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -442,6 +442,8 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) u16 *s2; u8 *s1; int i; + unsigned long ramdisk_addr; + unsigned long ramdisk_size; sys_table = _table; @@ -500,9 +502,14 @@ struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) memset(sdt, 0, sizeof(*sdt)); - status = handle_ramdisks(sys_table, image, hdr); + status = handle_cmdline_files(sys_table, image, + (char *)(unsigned long)hdr->cmd_line_ptr, + "initrd=", hdr->initrd_addr_max, + &ramdisk_addr, &ramdisk_size); if (status != EFI_SUCCESS) goto fail2; + hdr->ramdisk_image = ramdisk_addr; + hdr->ramdisk_size = ramdisk_size; return boot_params; fail2: diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index fda510fa3579..8c7839437c14 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -269,9 +269,12 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, * We only support loading an initrd from the same filesystem as the * kernel image. */ -static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, - efi_loaded_image_t *image, - struct setup_header *hdr) +static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, + efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size) { struct initrd *initrds; unsigned long initrd_addr; @@ -287,19 +290,25 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, initrd_addr = 0; initrd_total = 0; - str = (char *)(unsigned long)hdr->cmd_line_ptr; + str = cmd_line; j = 0; /* See close_handles */ + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + *load_addr = 0; + *load_size = 0; + if (!str || !*str) return EFI_SUCCESS; for (nr_initrds = 0; *str; nr_initrds++) { - str = strstr(str, "initrd="); + str = strstr(str, option_string); if (!str) break; - str += 7; + str += strlen(option_string); /* Skip any leading slashes */ while (*str == '/' || *str == '\\') @@ -317,11 +326,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, nr_initrds * sizeof(*initrds), &initrds); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file load\n"); goto fail; } - str = (char *)(unsigned long)hdr->cmd_line_ptr; + str = cmd_line; for (i = 0; i < nr_initrds; i++) { struct initrd *initrd; efi_file_handle_t *h; @@ -332,11 +341,11 @@ static efi_status_t handle_ramdisks(efi_system_table_t *sys_table_arg, efi_char16_t *p; u64 file_sz; - str = strstr(str, "initrd="); + str = strstr(str, option_string); if (!str) break; - str += 7; + str += strlen(option_string); initrd = &initrds[i]; p = filename_16; @@ -382,7 +391,7 @@ static efi_status_t handle_ramdisks(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 initrd file: "); + efi_printk(sys_table_arg, "Failed to open file: "); efi_char16_printk(sys_table_arg, filename_16); efi_printk(sys_table_arg, "\n"); goto close_handles; @@ -394,7 +403,7 @@ static efi_status_t handle_ramdisks(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 initrd info size\n"); + efi_printk(sys_table_arg, "Failed to get file info size\n"); goto close_handles; } @@ -402,7 +411,7 @@ grow: status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, info_sz, &info); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for initrd info\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); goto close_handles; } @@ -418,7 +427,7 @@ grow: efi_call_phys1(sys_table_arg->boottime->free_pool, info); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to get initrd info\n"); + efi_printk(sys_table_arg, "Failed to get file info\n"); goto close_handles; } @@ -435,14 +444,14 @@ grow: * all the initrd's. */ status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, hdr->initrd_addr_max); + &initrd_addr, max_addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc highmem for initrds\n"); goto close_handles; } /* We've run out of free low memory. */ - if (initrd_addr > hdr->initrd_addr_max) { + if (initrd_addr > max_addr) { efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; goto free_initrd_total; @@ -463,7 +472,7 @@ grow: initrds[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to read initrd\n"); + efi_printk(sys_table_arg, "Failed to read file\n"); goto free_initrd_total; } addr += chunksize; @@ -477,8 +486,8 @@ grow: efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); - hdr->ramdisk_image = initrd_addr; - hdr->ramdisk_size = initrd_total; + *load_addr = initrd_addr; + *load_size = initrd_total; return status; @@ -491,8 +500,8 @@ close_handles: free_initrds: efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); fail: - hdr->ramdisk_image = 0; - hdr->ramdisk_size = 0; + *load_addr = 0; + *load_size = 0; return status; } From 36f8961c963683ac12282e422dfc3db806ee0c7e Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:40 -0700 Subject: [PATCH 19/26] efi: Renames in handle_cmdline_files() to complete generalization. Rename variables to be not initrd specific, as now the function loads arbitrary files. This change is exclusively renames and comment changes to reflect the generalization. Signed-off-by: Roy Franz Acked-by: Mark Salter Reviewed-by: Grant Likely Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 92 +++++++++++++------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 8c7839437c14..5cea5d5a9a1f 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -11,7 +11,7 @@ */ #define EFI_READ_CHUNK_SIZE (1024 * 1024) -struct initrd { +struct file_info { efi_file_handle_t *handle; u64 size; }; @@ -264,10 +264,10 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, /* - * Check the cmdline for a LILO-style initrd= arguments. + * Check the cmdline for a LILO-style file= arguments. * - * We only support loading an initrd from the same filesystem as the - * kernel image. + * We only support loading a file from the same filesystem as + * the kernel image. */ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, efi_loaded_image_t *image, @@ -276,19 +276,19 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, unsigned long *load_addr, unsigned long *load_size) { - struct initrd *initrds; - unsigned long initrd_addr; + struct file_info *files; + unsigned long file_addr; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; - u64 initrd_total; + u64 file_size_total; efi_file_io_interface_t *io; efi_file_handle_t *fh; efi_status_t status; - int nr_initrds; + int nr_files; char *str; int i, j, k; - initrd_addr = 0; - initrd_total = 0; + file_addr = 0; + file_size_total = 0; str = cmd_line; @@ -303,7 +303,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, if (!str || !*str) return EFI_SUCCESS; - for (nr_initrds = 0; *str; nr_initrds++) { + for (nr_files = 0; *str; nr_files++) { str = strstr(str, option_string); if (!str) break; @@ -318,21 +318,21 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, str++; } - if (!nr_initrds) + if (!nr_files) return EFI_SUCCESS; status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, - nr_initrds * sizeof(*initrds), - &initrds); + nr_files * sizeof(*files), + &files); if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for file load\n"); + efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); goto fail; } str = cmd_line; - for (i = 0; i < nr_initrds; i++) { - struct initrd *initrd; + for (i = 0; i < nr_files; i++) { + struct file_info *file; efi_file_handle_t *h; efi_file_info_t *info; efi_char16_t filename_16[256]; @@ -347,7 +347,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, str += strlen(option_string); - initrd = &initrds[i]; + file = &files[i]; p = filename_16; /* Skip any leading slashes */ @@ -378,13 +378,13 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, image->device_handle, &fs_proto, &io); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); - goto free_initrds; + 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"); - goto free_initrds; + goto free_files; } } @@ -397,7 +397,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, goto close_handles; } - initrd->handle = h; + file->handle = h; info_sz = 0; status = efi_call_phys4(h->get_info, h, &info_guid, @@ -431,37 +431,37 @@ grow: goto close_handles; } - initrd->size = file_sz; - initrd_total += file_sz; + file->size = file_sz; + file_size_total += file_sz; } - if (initrd_total) { + if (file_size_total) { unsigned long addr; /* - * Multiple initrd's need to be at consecutive - * addresses in memory, so allocate enough memory for - * all the initrd's. + * Multiple files need to be at consecutive addresses in memory, + * so allocate enough memory for all the files. This is used + * for loading multiple files. */ - status = efi_high_alloc(sys_table_arg, initrd_total, 0x1000, - &initrd_addr, max_addr); + 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 initrds\n"); + efi_printk(sys_table_arg, "Failed to alloc highmem for files\n"); goto close_handles; } /* We've run out of free low memory. */ - if (initrd_addr > max_addr) { + if (file_addr > max_addr) { efi_printk(sys_table_arg, "We've run out of free low memory\n"); status = EFI_INVALID_PARAMETER; - goto free_initrd_total; + goto free_file_total; } - addr = initrd_addr; - for (j = 0; j < nr_initrds; j++) { + addr = file_addr; + for (j = 0; j < nr_files; j++) { u64 size; - size = initrds[j].size; + size = files[j].size; while (size) { u64 chunksize; if (size > EFI_READ_CHUNK_SIZE) @@ -469,36 +469,36 @@ grow: else chunksize = size; status = efi_call_phys3(fh->read, - initrds[j].handle, + files[j].handle, &chunksize, addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to read file\n"); - goto free_initrd_total; + goto free_file_total; } addr += chunksize; size -= chunksize; } - efi_call_phys1(fh->close, initrds[j].handle); + efi_call_phys1(fh->close, files[j].handle); } } - efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); + efi_call_phys1(sys_table_arg->boottime->free_pool, files); - *load_addr = initrd_addr; - *load_size = initrd_total; + *load_addr = file_addr; + *load_size = file_size_total; return status; -free_initrd_total: - efi_free(sys_table_arg, initrd_total, initrd_addr); +free_file_total: + efi_free(sys_table_arg, file_size_total, file_addr); close_handles: for (k = j; k < i; k++) - efi_call_phys1(fh->close, initrds[k].handle); -free_initrds: - efi_call_phys1(sys_table_arg->boottime->free_pool, initrds); + efi_call_phys1(fh->close, files[k].handle); +free_files: + efi_call_phys1(sys_table_arg->boottime->free_pool, files); fail: *load_addr = 0; *load_size = 0; From 6a5fe770d32811ffacefaa2a430cc067ecc7336c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:41 -0700 Subject: [PATCH 20/26] efi: Fix types in EFI calls to match EFI function definitions. EFI calls can made directly on ARM, so the function pointers are directly invoked. This allows types to be checked at compile time, so here we ensure that the parameters match the function signature. The wrappers used by x86 prevent any type checking. Correct the type of chunksize to be based on native width as specified by the EFI_FILE_PROTOCOL read() function. Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 5cea5d5a9a1f..4252d01089b2 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -324,7 +324,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, EFI_LOADER_DATA, nr_files * sizeof(*files), - &files); + (void **)&files); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); goto fail; @@ -375,7 +375,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, boottime = sys_table_arg->boottime; status = efi_call_phys3(boottime->handle_protocol, - image->device_handle, &fs_proto, &io); + image->device_handle, &fs_proto, + (void **)&io); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); goto free_files; @@ -409,7 +410,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, grow: status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, - EFI_LOADER_DATA, info_sz, &info); + EFI_LOADER_DATA, info_sz, + (void **)&info); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); goto close_handles; @@ -459,18 +461,19 @@ grow: addr = file_addr; for (j = 0; j < nr_files; j++) { - u64 size; + unsigned long size; size = files[j].size; while (size) { - u64 chunksize; + unsigned long chunksize; if (size > EFI_READ_CHUNK_SIZE) chunksize = EFI_READ_CHUNK_SIZE; else chunksize = size; status = efi_call_phys3(fh->read, files[j].handle, - &chunksize, addr); + &chunksize, + (void *)addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to read file\n"); goto free_file_total; From 4e283088bd9af0ff119d6e15bca81a83d6e8e30c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:42 -0700 Subject: [PATCH 21/26] efi: resolve warnings found on ARM compile warnings from gcc: warning: label 'free_pool' defined but not used [-Wunused-label] warning: value computed is not used [-Wunused-value] Signed-off-by: Roy Franz Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 4252d01089b2..cc0581daa9ed 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -170,7 +170,6 @@ again: *addr = max_addr; } -free_pool: efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: @@ -244,7 +243,6 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if (i == map_size / desc_size) status = EFI_NOT_FOUND; -free_pool: efi_call_phys1(sys_table_arg->boottime->free_pool, map); fail: return status; @@ -360,7 +358,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, if (*str == '/') { *p++ = '\\'; - *str++; + str++; } else { *p++ = *str++; } From d2078d5adbe227d64d7963d93f13479c890a9a16 Mon Sep 17 00:00:00 2001 From: Linn Crosetto Date: Sun, 22 Sep 2013 19:59:08 -0600 Subject: [PATCH 22/26] x86: EFI stub support for large memory maps This patch fixes a problem with EFI memory maps larger than 128 entries when booting using the EFI stub, which results in overflowing e820_map in boot_params and an eventual halt when checking the map size in sanitize_e820_map(). If the number of map entries is greater than what can fit in e820_map, add the extra entries to the setup_data list using type SETUP_E820_EXT. These extra entries are then picked up when the setup_data list is parsed in parse_e820_ext(). Signed-off-by: Linn Crosetto Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 230 ++++++++++++++++++++++--------- 1 file changed, 165 insertions(+), 65 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index beb07a4529ac..5c0dc55f2387 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -519,71 +519,47 @@ fail: return NULL; } -static efi_status_t exit_boot(struct boot_params *boot_params, - void *handle) +static void add_e820ext(struct boot_params *params, + struct setup_data *e820ext, u32 nr_entries) { - struct efi_info *efi = &boot_params->efi_info; - struct e820entry *e820_map = &boot_params->e820_map[0]; - struct e820entry *prev = NULL; - unsigned long size, key, desc_size, _size; - efi_memory_desc_t *mem_map; + struct setup_data *data; efi_status_t status; - __u32 desc_version; - bool called_exit = false; - u8 nr_entries; + unsigned long size; + + e820ext->type = SETUP_E820_EXT; + e820ext->len = nr_entries * sizeof(struct e820entry); + e820ext->next = 0; + + data = (struct setup_data *)(unsigned long)params->hdr.setup_data; + + while (data && data->next) + data = (struct setup_data *)(unsigned long)data->next; + + if (data) + data->next = (unsigned long)e820ext; + else + params->hdr.setup_data = (unsigned long)e820ext; +} + +static efi_status_t setup_e820(struct boot_params *params, + struct setup_data *e820ext, u32 e820ext_size) +{ + struct e820entry *e820_map = ¶ms->e820_map[0]; + struct efi_info *efi = ¶ms->efi_info; + struct e820entry *prev = NULL; + u32 nr_entries; + u32 nr_desc; int i; -get_map: - status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, - &desc_version, &key); - - if (status != EFI_SUCCESS) - return status; - - memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); - efi->efi_systab = (unsigned long)sys_table; - efi->efi_memdesc_size = desc_size; - efi->efi_memdesc_version = desc_version; - efi->efi_memmap = (unsigned long)mem_map; - efi->efi_memmap_size = size; - -#ifdef CONFIG_X86_64 - efi->efi_systab_hi = (unsigned long)sys_table >> 32; - efi->efi_memmap_hi = (unsigned long)mem_map >> 32; -#endif - - /* Might as well exit boot services now */ - status = efi_call_phys2(sys_table->boottime->exit_boot_services, - handle, key); - if (status != EFI_SUCCESS) { - /* - * ExitBootServices() will fail if any of the event - * handlers change the memory map. In which case, we - * must be prepared to retry, but only once so that - * we're guaranteed to exit on repeated failures instead - * of spinning forever. - */ - if (called_exit) - goto free_mem_map; - - called_exit = true; - efi_call_phys1(sys_table->boottime->free_pool, mem_map); - goto get_map; - } - - /* Historic? */ - boot_params->alt_mem_k = 32 * 1024; - - /* - * Convert the EFI memory map to E820. - */ nr_entries = 0; - for (i = 0; i < size / desc_size; i++) { + nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size; + + for (i = 0; i < nr_desc; i++) { efi_memory_desc_t *d; unsigned int e820_type = 0; - unsigned long m = (unsigned long)mem_map; + unsigned long m = efi->efi_memmap; - d = (efi_memory_desc_t *)(m + (i * desc_size)); + d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); switch (d->type) { case EFI_RESERVED_TYPE: case EFI_RUNTIME_SERVICES_CODE: @@ -620,18 +596,142 @@ get_map: /* Merge adjacent mappings */ if (prev && prev->type == e820_type && - (prev->addr + prev->size) == d->phys_addr) + (prev->addr + prev->size) == d->phys_addr) { prev->size += d->num_pages << 12; - else { - e820_map->addr = d->phys_addr; - e820_map->size = d->num_pages << 12; - e820_map->type = e820_type; - prev = e820_map++; - nr_entries++; + continue; } + + if (nr_entries == ARRAY_SIZE(params->e820_map)) { + u32 need = (nr_desc - i) * sizeof(struct e820entry) + + sizeof(struct setup_data); + + if (!e820ext || e820ext_size < need) + return EFI_BUFFER_TOO_SMALL; + + /* boot_params map full, switch to e820 extended */ + e820_map = (struct e820entry *)e820ext->data; + } + + e820_map->addr = d->phys_addr; + e820_map->size = d->num_pages << PAGE_SHIFT; + e820_map->type = e820_type; + prev = e820_map++; + nr_entries++; } - boot_params->e820_entries = nr_entries; + if (nr_entries > ARRAY_SIZE(params->e820_map)) { + u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map); + + add_e820ext(params, e820ext, nr_e820ext); + nr_entries -= nr_e820ext; + } + + params->e820_entries = (u8)nr_entries; + + return EFI_SUCCESS; +} + +static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, + u32 *e820ext_size) +{ + efi_status_t status; + unsigned long size; + + size = sizeof(struct setup_data) + + sizeof(struct e820entry) * nr_desc; + + if (*e820ext) { + efi_call_phys1(sys_table->boottime->free_pool, *e820ext); + *e820ext = NULL; + *e820ext_size = 0; + } + + status = efi_call_phys3(sys_table->boottime->allocate_pool, + EFI_LOADER_DATA, size, e820ext); + + if (status == EFI_SUCCESS) + *e820ext_size = size; + + return status; +} + +static efi_status_t exit_boot(struct boot_params *boot_params, + void *handle) +{ + struct efi_info *efi = &boot_params->efi_info; + unsigned long map_sz, key, desc_size; + efi_memory_desc_t *mem_map; + struct setup_data *e820ext; + __u32 e820ext_size; + __u32 nr_desc, prev_nr_desc; + efi_status_t status; + __u32 desc_version; + bool called_exit = false; + u8 nr_entries; + int i; + + nr_desc = 0; + e820ext = NULL; + e820ext_size = 0; + +get_map: + status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, + &desc_version, &key); + + if (status != EFI_SUCCESS) + return status; + + prev_nr_desc = nr_desc; + nr_desc = map_sz / desc_size; + if (nr_desc > prev_nr_desc && + nr_desc > ARRAY_SIZE(boot_params->e820_map)) { + u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map); + + status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size); + if (status != EFI_SUCCESS) + goto free_mem_map; + + efi_call_phys1(sys_table->boottime->free_pool, mem_map); + goto get_map; /* Allocated memory, get map again */ + } + + memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); + efi->efi_systab = (unsigned long)sys_table; + efi->efi_memdesc_size = desc_size; + efi->efi_memdesc_version = desc_version; + efi->efi_memmap = (unsigned long)mem_map; + efi->efi_memmap_size = map_sz; + +#ifdef CONFIG_X86_64 + efi->efi_systab_hi = (unsigned long)sys_table >> 32; + efi->efi_memmap_hi = (unsigned long)mem_map >> 32; +#endif + + /* Might as well exit boot services now */ + status = efi_call_phys2(sys_table->boottime->exit_boot_services, + handle, key); + if (status != EFI_SUCCESS) { + /* + * ExitBootServices() will fail if any of the event + * handlers change the memory map. In which case, we + * must be prepared to retry, but only once so that + * we're guaranteed to exit on repeated failures instead + * of spinning forever. + */ + if (called_exit) + goto free_mem_map; + + called_exit = true; + efi_call_phys1(sys_table->boottime->free_pool, mem_map); + goto get_map; + } + + /* Historic? */ + boot_params->alt_mem_k = 32 * 1024; + + status = setup_e820(boot_params, e820ext, e820ext_size); + if (status != EFI_SUCCESS) + return status; return EFI_SUCCESS; From 0ce6cda2c75d64175394341ef60e6e1d27dd9c10 Mon Sep 17 00:00:00 2001 From: Bart Kuivenhoven Date: Mon, 23 Sep 2013 11:45:28 +0200 Subject: [PATCH 23/26] x86 efi: bugfix interrupt disabling sequence The problem in efi_main was that the idt was cleared before the interrupts were disabled. The UEFI spec states that interrupts aren't used so this shouldn't be too much of a problem. Peripherals however don't necessarily know about this and thus might cause interrupts to happen anyway. Even if ExitBootServices() has been called. This means there is a risk of an interrupt being triggered while the IDT register is nullified and the interrupt bit hasn't been cleared, allowing for a triple fault. This patch disables the interrupt flag, while leaving the existing IDT in place. The CPU won't care about the IDT at all as long as the interrupt bit is off, so it's safe to leave it in place as nothing will ever happen to it. [ Removed the now unused 'idt' variable - Matt ] Signed-off-by: Bart Kuivenhoven Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 5c0dc55f2387..a7677babf946 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -748,7 +748,7 @@ free_mem_map: struct boot_params *efi_main(void *handle, efi_system_table_t *_table, struct boot_params *boot_params) { - struct desc_ptr *gdt, *idt; + struct desc_ptr *gdt; efi_loaded_image_t *image; struct setup_header *hdr = &boot_params->hdr; efi_status_t status; @@ -780,17 +780,6 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, goto fail; } - status = efi_call_phys3(sys_table->boottime->allocate_pool, - EFI_LOADER_DATA, sizeof(*idt), - (void **)&idt); - if (status != EFI_SUCCESS) { - efi_printk(sys_table, "Failed to alloc mem for idt structure\n"); - goto fail; - } - - idt->size = 0; - idt->address = 0; - /* * If the kernel isn't already loaded at the preferred load * address, relocate it. @@ -865,10 +854,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, desc->base2 = 0x00; #endif /* CONFIG_X86_64 */ - asm volatile ("lidt %0" : : "m" (*idt)); - asm volatile ("lgdt %0" : : "m" (*gdt)); - asm volatile("cli"); + asm volatile ("lgdt %0" : : "m" (*gdt)); return boot_params; fail: From 722da9d20e7f203ddbb54aba32f95a020d41fc42 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 3 Oct 2013 15:42:37 +0100 Subject: [PATCH 24/26] x86/efi: Fix config_table_type array termination Incorrect use of 0 in terminating entry of arch_tables[] causes the following sparse warning, arch/x86/platform/efi/efi.c:74:27: sparse: Using plain integer as NULL pointer Replace with NULL. Signed-off-by: Leif Lindholm [ Included sparse warning in commit message. ] Signed-off-by: Matt Fleming --- arch/x86/platform/efi/efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index fbc1d70188f8..1d3372ac9c66 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -71,7 +71,7 @@ static __initdata efi_config_table_type_t arch_tables[] = { #ifdef CONFIG_X86_UV {UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab}, #endif - {NULL_GUID, NULL, 0}, + {NULL_GUID, NULL, NULL}, }; /* From c158c3bf59951bbb44bd7ccca9e6665dfd1617c5 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Fri, 4 Oct 2013 09:02:46 -0700 Subject: [PATCH 25/26] boot, efi: Remove redundant memset() Remove a redundant memset() call from efi_relocate_kernel() that was clearing memory that would be used by BSS in non-compressed images loaded with this function. This clear was redundant with the clearing done in the image itself, and also implemented incorrectly with a 0 length. Signed-off-by: Roy Franz Acked-by: Mark Salter Signed-off-by: Matt Fleming --- drivers/firmware/efi/efi-stub-helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index cc0581daa9ed..b6bffbfd3be7 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -567,8 +567,6 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, * have been allocated by UEFI, so we can safely use memcpy. */ memcpy((void *)new_addr, (void *)cur_image_addr, image_size); - /* Zero any extra space we may have allocated for BSS. */ - memset((void *)(new_addr + image_size), alloc_size - image_size, 0); /* Return the new address of the relocated image. */ *image_addr = new_addr; From 72548e836b0c4abbb652e791dee9c91203a9a4c6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 4 Oct 2013 09:36:56 +0100 Subject: [PATCH 26/26] x86/efi: Add EFI framebuffer earlyprintk support It's incredibly difficult to diagnose early EFI boot issues without special hardware because earlyprintk=vga doesn't work on EFI systems. Add support for writing to the EFI framebuffer, via earlyprintk=efi, which will actually give users a chance of providing debug output. Cc: H. Peter Anvin Acked-by: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Jones Signed-off-by: Matt Fleming --- Documentation/kernel-parameters.txt | 8 +- arch/x86/Kconfig.debug | 10 ++ arch/x86/include/asm/efi.h | 2 + arch/x86/kernel/early_printk.c | 7 + arch/x86/platform/efi/Makefile | 1 + arch/x86/platform/efi/early_printk.c | 191 +++++++++++++++++++++++++++ 6 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 arch/x86/platform/efi/early_printk.c diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7f9d4f53882c..7a0f202d482e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -792,6 +792,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. earlyprintk= [X86,SH,BLACKFIN,ARM] earlyprintk=vga + earlyprintk=efi earlyprintk=xen earlyprintk=serial[,ttySn[,baudrate]] earlyprintk=serial[,0x...[,baudrate]] @@ -805,7 +806,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Append ",keep" to not disable it when the real console takes over. - Only vga or serial or usb debug port at a time. + Only one of vga, efi, serial, or usb debug port can + be used at a time. Currently only ttyS0 and ttyS1 may be specified by name. Other I/O ports may be explicitly specified @@ -819,8 +821,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Interaction with the standard serial driver is not very good. - The VGA output is eventually overwritten by the real - console. + The VGA and EFI output is eventually overwritten by + the real console. The xen output can only be used by Xen PV guests. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 78d91afb8e50..0f3621ed1db6 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -59,6 +59,16 @@ config EARLY_PRINTK_DBGP with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. You need usb debug device. +config EARLY_PRINTK_EFI + bool "Early printk via the EFI framebuffer" + depends on EFI && EARLY_PRINTK + select FONT_SUPPORT + ---help--- + Write kernel log output directly into the EFI framebuffer. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. + config X86_PTDUMP bool "Export kernel pagetable layout to userspace via debugfs" depends on DEBUG_KERNEL diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 0062a0125041..65c6e6e3a552 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -109,6 +109,8 @@ static inline bool efi_is_native(void) return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); } +extern struct console early_efi_console; + #else /* * IF EFI is not configured, have the EFI calls return -ENOSYS. diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index d15f575a861b..6d3d20073615 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -234,6 +236,11 @@ static int __init setup_early_printk(char *buf) early_console_register(&early_hsu_console, keep); } #endif +#ifdef CONFIG_EARLY_PRINTK_EFI + if (!strncmp(buf, "efi", 3)) + early_console_register(&early_efi_console, keep); +#endif + buf++; } return 0; diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 6db1cc4c7534..b7b0b35c1981 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o +obj-$(CONFIG_EARLY_PRINTK_EFI) += early_printk.o diff --git a/arch/x86/platform/efi/early_printk.c b/arch/x86/platform/efi/early_printk.c new file mode 100644 index 000000000000..6599a0027b76 --- /dev/null +++ b/arch/x86/platform/efi/early_printk.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2013 Intel Corporation; author Matt Fleming + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +static const struct font_desc *font; +static u32 efi_x, efi_y; + +static __init void early_efi_clear_scanline(unsigned int y) +{ + unsigned long base, *dst; + u16 len; + + base = boot_params.screen_info.lfb_base; + len = boot_params.screen_info.lfb_linelength; + + dst = early_ioremap(base + y*len, len); + if (!dst) + return; + + memset(dst, 0, len); + early_iounmap(dst, len); +} + +static __init void early_efi_scroll_up(void) +{ + unsigned long base, *dst, *src; + u16 len; + u32 i, height; + + base = boot_params.screen_info.lfb_base; + len = boot_params.screen_info.lfb_linelength; + height = boot_params.screen_info.lfb_height; + + for (i = 0; i < height - font->height; i++) { + dst = early_ioremap(base + i*len, len); + if (!dst) + return; + + src = early_ioremap(base + (i + font->height) * len, len); + if (!src) { + early_iounmap(dst, len); + return; + } + + memmove(dst, src, len); + + early_iounmap(src, len); + early_iounmap(dst, len); + } +} + +static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) +{ + const u32 color_black = 0x00000000; + const u32 color_white = 0x00ffffff; + const u8 *src; + u8 s8; + int m; + + src = font->data + c * font->height; + s8 = *(src + h); + + for (m = 0; m < 8; m++) { + if ((s8 >> (7 - m)) & 1) + *dst = color_white; + else + *dst = color_black; + dst++; + } +} + +static __init void +early_efi_write(struct console *con, const char *str, unsigned int num) +{ + struct screen_info *si; + unsigned long base; + unsigned int len; + const char *s; + void *dst; + + base = boot_params.screen_info.lfb_base; + si = &boot_params.screen_info; + len = si->lfb_linelength; + + while (num) { + unsigned int linemax; + unsigned int h, count = 0; + + for (s = str; *s && *s != '\n'; s++) { + if (count == num) + break; + count++; + } + + linemax = (si->lfb_width - efi_x) / font->width; + if (count > linemax) + count = linemax; + + for (h = 0; h < font->height; h++) { + unsigned int n, x; + + dst = early_ioremap(base + (efi_y + h) * len, len); + if (!dst) + return; + + s = str; + n = count; + x = efi_x; + + while (n-- > 0) { + early_efi_write_char(dst + x*4, *s, h); + x += font->width; + s++; + } + + early_iounmap(dst, len); + } + + num -= count; + efi_x += count * font->width; + str += count; + + if (num > 0 && *s == '\n') { + efi_x = 0; + efi_y += font->height; + str++; + num--; + } + + if (efi_x >= si->lfb_width) { + efi_x = 0; + efi_y += font->height; + } + + if (efi_y + font->height >= si->lfb_height) { + u32 i; + + efi_y -= font->height; + early_efi_scroll_up(); + + for (i = 0; i < font->height; i++) + early_efi_clear_scanline(efi_y + i); + } + } +} + +static __init int early_efi_setup(struct console *con, char *options) +{ + struct screen_info *si; + u16 xres, yres; + u32 i; + + si = &boot_params.screen_info; + xres = si->lfb_width; + yres = si->lfb_height; + + /* + * early_efi_write_char() implicitly assumes a framebuffer with + * 32-bits per pixel. + */ + if (si->lfb_depth != 32) + return -ENODEV; + + font = get_default_font(xres, yres, -1, -1); + if (!font) + return -ENODEV; + + efi_y = rounddown(yres, font->height) - font->height; + for (i = 0; i < (yres - efi_y) / font->height; i++) + early_efi_scroll_up(); + + return 0; +} + +struct console early_efi_console = { + .name = "earlyefi", + .write = early_efi_write, + .setup = early_efi_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +};