From 272686bf46a34f86d270cf192f68769667792026 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:54 +0100 Subject: 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/x86/platform/efi/efi.c | 96 ++++----------------------------------------- 1 file changed, 8 insertions(+), 88 deletions(-) (limited to 'arch/x86') 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); -- cgit v1.2.3 From 258f6fd738221766b512cd8c7120563b78d62829 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 5 Sep 2013 11:34:55 +0100 Subject: 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(-) (limited to 'arch/x86') 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 -- cgit v1.2.3 From 4172fe2f8a479e2237459918edc83b027efa8808 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:25 -0700 Subject: 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/efi-stub.txt | 65 ++++++++++++++++++++++++++++++++++++++++++ Documentation/x86/efi-stub.txt | 65 ------------------------------------------ arch/x86/Kconfig | 2 +- 3 files changed, 66 insertions(+), 66 deletions(-) create mode 100644 Documentation/efi-stub.txt delete mode 100644 Documentation/x86/efi-stub.txt (limited to 'arch/x86') diff --git a/Documentation/efi-stub.txt b/Documentation/efi-stub.txt new file mode 100644 index 000000000000..44e6bb6ead10 --- /dev/null +++ b/Documentation/efi-stub.txt @@ -0,0 +1,65 @@ + The EFI Boot Stub + --------------------------- + +On the x86 platform, a bzImage can masquerade as a PE/COFF image, +thereby convincing EFI firmware loaders to load it as an EFI +executable. The code that modifies the bzImage header, along with the +EFI-specific entry point that the firmware loader jumps to are +collectively known as the "EFI boot stub", and live in +arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c, +respectively. + +By using the EFI boot stub it's possible to boot a Linux kernel +without the use of a conventional EFI boot loader, such as grub or +elilo. Since the EFI boot stub performs the jobs of a boot loader, in +a certain sense it *IS* the boot loader. + +The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option. + + +**** How to install bzImage.efi + +The bzImage located in arch/x86/boot/bzImage must be copied to the EFI +System Partiion (ESP) and renamed with the extension ".efi". Without +the extension the EFI firmware loader will refuse to execute it. It's +not possible to execute bzImage.efi from the usual Linux file systems +because EFI firmware doesn't have support for them. + + +**** Passing kernel parameters from the EFI shell + +Arguments to the kernel can be passed after bzImage.efi, e.g. + + fs0:> bzImage.efi console=ttyS0 root=/dev/sda4 + + +**** The "initrd=" option + +Like most boot loaders, the EFI stub allows the user to specify +multiple initrd files using the "initrd=" option. This is the only EFI +stub-specific command line parameter, everything else is passed to the +kernel when it boots. + +The path to the initrd file must be an absolute path from the +beginning of the ESP, relative path names do not work. Also, the path +is an EFI-style path and directory elements must be separated with +backslashes (\). For example, given the following directory layout, + +fs0:> + Kernels\ + bzImage.efi + initrd-large.img + + Ramdisks\ + initrd-small.img + initrd-medium.img + +to boot with the initrd-large.img file if the current working +directory is fs0:\Kernels, the following command must be used, + + fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img + +Notice how bzImage.efi can be specified with a relative path. That's +because the image we're executing is interpreted by the EFI shell, +which understands relative paths, whereas the rest of the command line +is passed to bzImage.efi. diff --git a/Documentation/x86/efi-stub.txt b/Documentation/x86/efi-stub.txt deleted file mode 100644 index 44e6bb6ead10..000000000000 --- a/Documentation/x86/efi-stub.txt +++ /dev/null @@ -1,65 +0,0 @@ - The EFI Boot Stub - --------------------------- - -On the x86 platform, a bzImage can masquerade as a PE/COFF image, -thereby convincing EFI firmware loaders to load it as an EFI -executable. The code that modifies the bzImage header, along with the -EFI-specific entry point that the firmware loader jumps to are -collectively known as the "EFI boot stub", and live in -arch/x86/boot/header.S and arch/x86/boot/compressed/eboot.c, -respectively. - -By using the EFI boot stub it's possible to boot a Linux kernel -without the use of a conventional EFI boot loader, such as grub or -elilo. Since the EFI boot stub performs the jobs of a boot loader, in -a certain sense it *IS* the boot loader. - -The EFI boot stub is enabled with the CONFIG_EFI_STUB kernel option. - - -**** How to install bzImage.efi - -The bzImage located in arch/x86/boot/bzImage must be copied to the EFI -System Partiion (ESP) and renamed with the extension ".efi". Without -the extension the EFI firmware loader will refuse to execute it. It's -not possible to execute bzImage.efi from the usual Linux file systems -because EFI firmware doesn't have support for them. - - -**** Passing kernel parameters from the EFI shell - -Arguments to the kernel can be passed after bzImage.efi, e.g. - - fs0:> bzImage.efi console=ttyS0 root=/dev/sda4 - - -**** The "initrd=" option - -Like most boot loaders, the EFI stub allows the user to specify -multiple initrd files using the "initrd=" option. This is the only EFI -stub-specific command line parameter, everything else is passed to the -kernel when it boots. - -The path to the initrd file must be an absolute path from the -beginning of the ESP, relative path names do not work. Also, the path -is an EFI-style path and directory elements must be separated with -backslashes (\). For example, given the following directory layout, - -fs0:> - Kernels\ - bzImage.efi - initrd-large.img - - Ramdisks\ - initrd-small.img - initrd-medium.img - -to boot with the initrd-large.img file if the current working -directory is fs0:\Kernels, the following command must be used, - - fs0:\Kernels> bzImage.efi initrd=\Kernels\initrd-large.img - -Notice how bzImage.efi can be specified with a relative path. That's -because the image we're executing is interpreted by the EFI shell, -which understands relative paths, whereas the rest of the command line -is passed to bzImage.efi. 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 -- cgit v1.2.3 From ed37ddffe201bfad7be3c45bc08bd65b5298adca Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:26 -0700 Subject: 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(-) (limited to 'arch/x86') 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 -- cgit v1.2.3 From 7721da4c1ebf96ff815987011c1a0edef596b50a Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:27 -0700 Subject: 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 (limited to 'arch/x86') 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); -} - -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); +#include "../../../../drivers/firmware/efi/efi-stub-helper.c" - 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; +} -- cgit v1.2.3 From 876dc36aceb1682cb01426f507f369aec4f68c76 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:28 -0700 Subject: 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(-) (limited to 'arch/x86') 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; -- cgit v1.2.3 From 40e4530a00b7d52332fe9a1361388fda8e5260da Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:29 -0700 Subject: 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(-) (limited to 'arch/x86') 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++) -- cgit v1.2.3 From c6866d7238d4f26055de8db9c1d5a700e554b2d4 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:31 -0700 Subject: 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(-) (limited to 'arch/x86') 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; +} -- cgit v1.2.3 From 4a9f3a7c336a6b0ffeef2523bef93e67b0921163 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:32 -0700 Subject: 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(-) (limited to 'arch/x86') 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 - * possible. + * 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. */ - 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, + 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, &start); + nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as + * possible. + */ 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; } -- cgit v1.2.3 From 5fef3870c572a30f9def07c4ded69dc37a9cf5d9 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:33 -0700 Subject: 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(-) (limited to 'arch/x86') 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; +} -- cgit v1.2.3 From ae8e9060a3ecfd22bd5059e39f81547613ae376c Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:37 -0700 Subject: 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(-) (limited to 'arch/x86') 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,25 +527,12 @@ static efi_status_t exit_boot(struct boot_params *boot_params, u8 nr_entries; int i; - size = sizeof(*mem_map) * 32; - -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; - } + status = efi_get_memory_map(sys_table, &mem_map, &size, &desc_size, + &desc_version, &key); if (status != EFI_SUCCESS) - goto free_mem_map; + return status; memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); efi->efi_systab = (unsigned long)sys_table; @@ -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; } -- cgit v1.2.3 From 0e1cadb05bba2293b4575c8cab275313d181d94f Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:38 -0700 Subject: 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(-) (limited to 'arch/x86') 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); } -- cgit v1.2.3 From 46f4582e7cbc5f30127183812d4da875782518f5 Mon Sep 17 00:00:00 2001 From: Roy Franz Date: Sun, 22 Sep 2013 15:45:39 -0700 Subject: 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(-) (limited to 'arch/x86') 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; } -- cgit v1.2.3 From d2078d5adbe227d64d7963d93f13479c890a9a16 Mon Sep 17 00:00:00 2001 From: Linn Crosetto Date: Sun, 22 Sep 2013 19:59:08 -0600 Subject: 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 | 222 ++++++++++++++++++++++++++++----------- 1 file changed, 161 insertions(+), 61 deletions(-) (limited to 'arch/x86') 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; - 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; + unsigned long size; - 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; + e820ext->type = SETUP_E820_EXT; + e820ext->len = nr_entries * sizeof(struct e820entry); + e820ext->next = 0; -#ifdef CONFIG_X86_64 - efi->efi_systab_hi = (unsigned long)sys_table >> 32; - efi->efi_memmap_hi = (unsigned long)mem_map >> 32; -#endif + data = (struct setup_data *)(unsigned long)params->hdr.setup_data; - /* 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; + while (data && data->next) + data = (struct setup_data *)(unsigned long)data->next; - called_exit = true; - efi_call_phys1(sys_table->boottime->free_pool, mem_map); - goto get_map; - } + if (data) + data->next = (unsigned long)e820ext; + else + params->hdr.setup_data = (unsigned long)e820ext; +} - /* Historic? */ - boot_params->alt_mem_k = 32 * 1024; +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; - /* - * 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; -- cgit v1.2.3 From 0ce6cda2c75d64175394341ef60e6e1d27dd9c10 Mon Sep 17 00:00:00 2001 From: Bart Kuivenhoven Date: Mon, 23 Sep 2013 11:45:28 +0200 Subject: 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(-) (limited to 'arch/x86') 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: -- cgit v1.2.3 From 722da9d20e7f203ddbb54aba32f95a020d41fc42 Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Thu, 3 Oct 2013 15:42:37 +0100 Subject: 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(-) (limited to 'arch/x86') 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}, }; /* -- cgit v1.2.3 From 72548e836b0c4abbb652e791dee9c91203a9a4c6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 4 Oct 2013 09:36:56 +0100 Subject: 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 (limited to 'arch/x86') 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, +}; -- cgit v1.2.3