From 2e0eb483c058dd013be8e3d0ec1767be531485a2 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 15 Apr 2020 12:54:18 -0700 Subject: efi/libstub: Move arm-stub to a common file Most of the arm-stub code is written in an architecture independent manner. As a result, RISC-V can reuse most of the arm-stub code. Rename the arm-stub.c to efi-stub.c so that ARM, ARM64 and RISC-V can use it. This patch doesn't introduce any functional changes. Signed-off-by: Atish Patra Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20200415195422.19866-2-atish.patra@wdc.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 4 +- drivers/firmware/efi/libstub/Makefile | 12 +- drivers/firmware/efi/libstub/arm-stub.c | 408 -------------------------------- drivers/firmware/efi/libstub/efi-stub.c | 408 ++++++++++++++++++++++++++++++++ 4 files changed, 416 insertions(+), 416 deletions(-) delete mode 100644 drivers/firmware/efi/libstub/arm-stub.c create mode 100644 drivers/firmware/efi/libstub/efi-stub.c (limited to 'drivers') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 613828d3f106..2a2b2b96a1dc 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -106,12 +106,12 @@ config EFI_PARAMS_FROM_FDT config EFI_RUNTIME_WRAPPERS bool -config EFI_ARMSTUB +config EFI_GENERIC_STUB bool config EFI_ARMSTUB_DTB_LOADER bool "Enable the DTB loader" - depends on EFI_ARMSTUB + depends on EFI_GENERIC_STUB default y help Select this config option to add support for the dtb= command diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 094eabdecfe6..75cb2c3a1519 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -23,7 +23,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) -cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt +cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \ @@ -45,13 +45,13 @@ lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ skip_spaces.o lib-cmdline.o lib-ctype.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 -arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c +efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \ - $(patsubst %.c,lib-%.o,$(arm-deps-y)) +lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o fdt.o string.o \ + $(patsubst %.c,lib-%.o,$(efi-deps-y)) lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o @@ -73,8 +73,8 @@ CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) # a verification pass to see if any absolute relocations exist in any of the # object files. # -extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y) -lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y)) +extra-$(CONFIG_EFI_GENERIC_STUB) := $(lib-y) +lib-$(CONFIG_EFI_GENERIC_STUB) := $(patsubst %.o,%.stub.o,$(lib-y)) STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c deleted file mode 100644 index 99a5cde7c2d8..000000000000 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ /dev/null @@ -1,408 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * EFI stub implementation that is shared by arm and arm64 architectures. - * This should be #included by the EFI stub implementation files. - * - * Copyright (C) 2013,2014 Linaro Limited - * Roy Franz - */ - -#include -#include -#include - -#include "efistub.h" - -/* - * This is the base address at which to start allocating virtual memory ranges - * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use - * any allocation we choose, and eliminate the risk of a conflict after kexec. - * The value chosen is the largest non-zero power of 2 suitable for this purpose - * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can - * be mapped efficiently. - * Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split, - * map everything below 1 GB. (512 MB is a reasonable upper bound for the - * entire footprint of the UEFI runtime services memory regions) - */ -#define EFI_RT_VIRTUAL_BASE SZ_512M -#define EFI_RT_VIRTUAL_SIZE SZ_512M - -#ifdef CONFIG_ARM64 -# define EFI_RT_VIRTUAL_LIMIT DEFAULT_MAP_WINDOW_64 -#else -# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE -#endif - -static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; -static bool __efistub_global flat_va_mapping; - -static efi_system_table_t *__efistub_global sys_table; - -__pure efi_system_table_t *efi_system_table(void) -{ - return sys_table; -} - -static struct screen_info *setup_graphics(void) -{ - efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; - efi_status_t status; - unsigned long size; - void **gop_handle = NULL; - struct screen_info *si = NULL; - - size = 0; - status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, - &gop_proto, NULL, &size, gop_handle); - if (status == EFI_BUFFER_TOO_SMALL) { - si = alloc_screen_info(); - if (!si) - return NULL; - efi_setup_gop(si, &gop_proto, size); - } - return si; -} - -void install_memreserve_table(void) -{ - struct linux_efi_memreserve *rsv; - efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; - efi_status_t status; - - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), - (void **)&rsv); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate memreserve entry!\n"); - return; - } - - rsv->next = 0; - rsv->size = 0; - atomic_set(&rsv->count, 0); - - status = efi_bs_call(install_configuration_table, - &memreserve_table_guid, rsv); - if (status != EFI_SUCCESS) - pr_efi_err("Failed to install memreserve config table!\n"); -} - -static unsigned long get_dram_base(void) -{ - efi_status_t status; - unsigned long map_size, buff_size; - unsigned long membase = EFI_ERROR; - struct efi_memory_map map; - efi_memory_desc_t *md; - struct efi_boot_memmap boot_map; - - boot_map.map = (efi_memory_desc_t **)&map.map; - boot_map.map_size = &map_size; - boot_map.desc_size = &map.desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - return membase; - - map.map_end = map.map + map_size; - - for_each_efi_memory_desc_in_map(&map, md) { - if (md->attribute & EFI_MEMORY_WB) { - if (membase > md->phys_addr) - membase = md->phys_addr; - } - } - - efi_bs_call(free_pool, map.map); - - return membase; -} - -/* - * This function handles the architcture specific differences between arm and - * arm64 regarding where the kernel image must be loaded and any memory that - * must be reserved. On failure it is required to free all - * all allocations it has made. - */ -efi_status_t handle_kernel_image(unsigned long *image_addr, - unsigned long *image_size, - unsigned long *reserve_addr, - unsigned long *reserve_size, - unsigned long dram_base, - efi_loaded_image_t *image); - -asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, - unsigned long fdt_addr, - unsigned long fdt_size); - -/* - * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint - * that is described in the PE/COFF header. Most of the code is the same - * for both archictectures, with the arch-specific code provided in the - * handle_kernel_image() function. - */ -efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) -{ - efi_loaded_image_t *image; - efi_status_t status; - unsigned long image_addr; - unsigned long image_size = 0; - unsigned long dram_base; - /* addr/point and size pairs for memory management*/ - unsigned long initrd_addr = 0; - unsigned long initrd_size = 0; - unsigned long fdt_addr = 0; /* Original DTB */ - unsigned long fdt_size = 0; - char *cmdline_ptr = NULL; - int cmdline_size = 0; - efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; - unsigned long reserve_addr = 0; - unsigned long reserve_size = 0; - enum efi_secureboot_mode secure_boot; - struct screen_info *si; - efi_properties_table_t *prop_tbl; - unsigned long max_addr; - - sys_table = sys_table_arg; - - /* Check if we were booted by the EFI firmware */ - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - status = EFI_INVALID_PARAMETER; - goto fail; - } - - status = check_platform_features(); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Get a handle to the loaded image protocol. This is used to get - * information about the running image, such as size and the command - * line. - */ - status = sys_table->boottime->handle_protocol(handle, - &loaded_image_proto, (void *)&image); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to get loaded image protocol\n"); - goto fail; - } - - dram_base = get_dram_base(); - if (dram_base == EFI_ERROR) { - pr_efi_err("Failed to find DRAM base\n"); - status = EFI_LOAD_ERROR; - goto fail; - } - - /* - * Get the command line from EFI, using the LOADED_IMAGE - * protocol. We are going to copy the command line into the - * device tree, so this can be allocated anywhere. - */ - cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); - if (!cmdline_ptr) { - pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); - status = EFI_OUT_OF_RESOURCES; - goto fail; - } - - if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || - IS_ENABLED(CONFIG_CMDLINE_FORCE) || - cmdline_size == 0) - efi_parse_options(CONFIG_CMDLINE); - - if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) - efi_parse_options(cmdline_ptr); - - pr_efi("Booting Linux Kernel...\n"); - - si = setup_graphics(); - - status = handle_kernel_image(&image_addr, &image_size, - &reserve_addr, - &reserve_size, - dram_base, image); - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to relocate kernel\n"); - goto fail_free_cmdline; - } - - efi_retrieve_tpm2_eventlog(); - - /* Ask the firmware to clear memory on unclean shutdown */ - efi_enable_reset_attack_mitigation(); - - secure_boot = efi_get_secureboot(); - - /* - * Unauthenticated device tree data is a security hazard, so ignore - * 'dtb=' unless UEFI Secure Boot is disabled. We assume that secure - * boot is enabled if we can't determine its state. - */ - if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) || - secure_boot != efi_secureboot_mode_disabled) { - if (strstr(cmdline_ptr, "dtb=")) - pr_efi("Ignoring DTB from command line.\n"); - } else { - status = efi_load_dtb(image, &fdt_addr, &fdt_size); - - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to load device tree!\n"); - goto fail_free_image; - } - } - - if (fdt_addr) { - pr_efi("Using DTB from command line\n"); - } else { - /* Look for a device tree configuration table entry. */ - fdt_addr = (uintptr_t)get_fdt(&fdt_size); - if (fdt_addr) - pr_efi("Using DTB from configuration table\n"); - } - - if (!fdt_addr) - pr_efi("Generating empty DTB\n"); - - if (!noinitrd()) { - max_addr = efi_get_max_initrd_addr(dram_base, image_addr); - status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, - max_addr); - if (status == EFI_SUCCESS) { - pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); - } else if (status == EFI_NOT_FOUND) { - status = efi_load_initrd(image, &initrd_addr, &initrd_size, - ULONG_MAX, max_addr); - if (status == EFI_SUCCESS && initrd_size > 0) - pr_efi("Loaded initrd from command line option\n"); - } - if (status != EFI_SUCCESS) - pr_efi_err("Failed to load initrd!\n"); - } - - efi_random_get_seed(); - - /* - * If the NX PE data feature is enabled in the properties table, we - * should take care not to create a virtual mapping that changes the - * relative placement of runtime services code and data regions, as - * they may belong to the same PE/COFF executable image in memory. - * The easiest way to achieve that is to simply use a 1:1 mapping. - */ - prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID); - flat_va_mapping = prop_tbl && - (prop_tbl->memory_protection_attribute & - EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); - - /* hibernation expects the runtime regions to stay in the same place */ - if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) { - /* - * Randomize the base of the UEFI runtime services region. - * Preserve the 2 MB alignment of the region by taking a - * shift of 21 bit positions into account when scaling - * the headroom value using a 32-bit random value. - */ - static const u64 headroom = EFI_RT_VIRTUAL_LIMIT - - EFI_RT_VIRTUAL_BASE - - EFI_RT_VIRTUAL_SIZE; - u32 rnd; - - status = efi_get_random_bytes(sizeof(rnd), (u8 *)&rnd); - if (status == EFI_SUCCESS) { - virtmap_base = EFI_RT_VIRTUAL_BASE + - (((headroom >> 21) * rnd) >> (32 - 21)); - } - } - - install_memreserve_table(); - - status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr, - efi_get_max_fdt_addr(dram_base), - initrd_addr, initrd_size, - cmdline_ptr, fdt_addr, fdt_size); - if (status != EFI_SUCCESS) - goto fail_free_initrd; - - efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr)); - /* not reached */ - -fail_free_initrd: - pr_efi_err("Failed to update FDT and exit boot services\n"); - - efi_free(initrd_size, initrd_addr); - efi_free(fdt_size, fdt_addr); - -fail_free_image: - efi_free(image_size, image_addr); - efi_free(reserve_size, reserve_addr); -fail_free_cmdline: - free_screen_info(si); - efi_free(cmdline_size, (unsigned long)cmdline_ptr); -fail: - return status; -} - -/* - * efi_get_virtmap() - create a virtual mapping for the EFI memory map - * - * This function populates the virt_addr fields of all memory region descriptors - * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors - * are also copied to @runtime_map, and their total count is returned in @count. - */ -void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, - unsigned long desc_size, efi_memory_desc_t *runtime_map, - int *count) -{ - u64 efi_virt_base = virtmap_base; - efi_memory_desc_t *in, *out = runtime_map; - int l; - - for (l = 0; l < map_size; l += desc_size) { - u64 paddr, size; - - in = (void *)memory_map + l; - if (!(in->attribute & EFI_MEMORY_RUNTIME)) - continue; - - paddr = in->phys_addr; - size = in->num_pages * EFI_PAGE_SIZE; - - in->virt_addr = in->phys_addr; - if (novamap()) { - continue; - } - - /* - * Make the mapping compatible with 64k pages: this allows - * a 4k page size kernel to kexec a 64k page size kernel and - * vice versa. - */ - if (!flat_va_mapping) { - - paddr = round_down(in->phys_addr, SZ_64K); - size += in->phys_addr - paddr; - - /* - * Avoid wasting memory on PTEs by choosing a virtual - * base that is compatible with section mappings if this - * region has the appropriate size and physical - * alignment. (Sections are 2 MB on 4k granule kernels) - */ - if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) - efi_virt_base = round_up(efi_virt_base, SZ_2M); - else - efi_virt_base = round_up(efi_virt_base, SZ_64K); - - in->virt_addr += efi_virt_base - paddr; - efi_virt_base += size; - } - - memcpy(out, in, desc_size); - out = (void *)out + desc_size; - ++*count; - } -} diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c new file mode 100644 index 000000000000..99a5cde7c2d8 --- /dev/null +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * EFI stub implementation that is shared by arm and arm64 architectures. + * This should be #included by the EFI stub implementation files. + * + * Copyright (C) 2013,2014 Linaro Limited + * Roy Franz + */ + +#include +#include +#include + +#include "efistub.h" + +/* + * This is the base address at which to start allocating virtual memory ranges + * for UEFI Runtime Services. This is in the low TTBR0 range so that we can use + * any allocation we choose, and eliminate the risk of a conflict after kexec. + * The value chosen is the largest non-zero power of 2 suitable for this purpose + * both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can + * be mapped efficiently. + * Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split, + * map everything below 1 GB. (512 MB is a reasonable upper bound for the + * entire footprint of the UEFI runtime services memory regions) + */ +#define EFI_RT_VIRTUAL_BASE SZ_512M +#define EFI_RT_VIRTUAL_SIZE SZ_512M + +#ifdef CONFIG_ARM64 +# define EFI_RT_VIRTUAL_LIMIT DEFAULT_MAP_WINDOW_64 +#else +# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE +#endif + +static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; +static bool __efistub_global flat_va_mapping; + +static efi_system_table_t *__efistub_global sys_table; + +__pure efi_system_table_t *efi_system_table(void) +{ + return sys_table; +} + +static struct screen_info *setup_graphics(void) +{ + efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + efi_status_t status; + unsigned long size; + void **gop_handle = NULL; + struct screen_info *si = NULL; + + size = 0; + status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, + &gop_proto, NULL, &size, gop_handle); + if (status == EFI_BUFFER_TOO_SMALL) { + si = alloc_screen_info(); + if (!si) + return NULL; + efi_setup_gop(si, &gop_proto, size); + } + return si; +} + +void install_memreserve_table(void) +{ + struct linux_efi_memreserve *rsv; + efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; + efi_status_t status; + + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), + (void **)&rsv); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to allocate memreserve entry!\n"); + return; + } + + rsv->next = 0; + rsv->size = 0; + atomic_set(&rsv->count, 0); + + status = efi_bs_call(install_configuration_table, + &memreserve_table_guid, rsv); + if (status != EFI_SUCCESS) + pr_efi_err("Failed to install memreserve config table!\n"); +} + +static unsigned long get_dram_base(void) +{ + efi_status_t status; + unsigned long map_size, buff_size; + unsigned long membase = EFI_ERROR; + struct efi_memory_map map; + efi_memory_desc_t *md; + struct efi_boot_memmap boot_map; + + boot_map.map = (efi_memory_desc_t **)&map.map; + boot_map.map_size = &map_size; + boot_map.desc_size = &map.desc_size; + boot_map.desc_ver = NULL; + boot_map.key_ptr = NULL; + boot_map.buff_size = &buff_size; + + status = efi_get_memory_map(&boot_map); + if (status != EFI_SUCCESS) + return membase; + + map.map_end = map.map + map_size; + + for_each_efi_memory_desc_in_map(&map, md) { + if (md->attribute & EFI_MEMORY_WB) { + if (membase > md->phys_addr) + membase = md->phys_addr; + } + } + + efi_bs_call(free_pool, map.map); + + return membase; +} + +/* + * This function handles the architcture specific differences between arm and + * arm64 regarding where the kernel image must be loaded and any memory that + * must be reserved. On failure it is required to free all + * all allocations it has made. + */ +efi_status_t handle_kernel_image(unsigned long *image_addr, + unsigned long *image_size, + unsigned long *reserve_addr, + unsigned long *reserve_size, + unsigned long dram_base, + efi_loaded_image_t *image); + +asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, + unsigned long fdt_addr, + unsigned long fdt_size); + +/* + * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint + * that is described in the PE/COFF header. Most of the code is the same + * for both archictectures, with the arch-specific code provided in the + * handle_kernel_image() function. + */ +efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) +{ + efi_loaded_image_t *image; + efi_status_t status; + unsigned long image_addr; + unsigned long image_size = 0; + unsigned long dram_base; + /* addr/point and size pairs for memory management*/ + unsigned long initrd_addr = 0; + unsigned long initrd_size = 0; + unsigned long fdt_addr = 0; /* Original DTB */ + unsigned long fdt_size = 0; + char *cmdline_ptr = NULL; + int cmdline_size = 0; + efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; + unsigned long reserve_addr = 0; + unsigned long reserve_size = 0; + enum efi_secureboot_mode secure_boot; + struct screen_info *si; + efi_properties_table_t *prop_tbl; + unsigned long max_addr; + + sys_table = sys_table_arg; + + /* Check if we were booted by the EFI firmware */ + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + status = EFI_INVALID_PARAMETER; + goto fail; + } + + status = check_platform_features(); + if (status != EFI_SUCCESS) + goto fail; + + /* + * Get a handle to the loaded image protocol. This is used to get + * information about the running image, such as size and the command + * line. + */ + status = sys_table->boottime->handle_protocol(handle, + &loaded_image_proto, (void *)&image); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to get loaded image protocol\n"); + goto fail; + } + + dram_base = get_dram_base(); + if (dram_base == EFI_ERROR) { + pr_efi_err("Failed to find DRAM base\n"); + status = EFI_LOAD_ERROR; + goto fail; + } + + /* + * Get the command line from EFI, using the LOADED_IMAGE + * protocol. We are going to copy the command line into the + * device tree, so this can be allocated anywhere. + */ + cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); + if (!cmdline_ptr) { + pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); + status = EFI_OUT_OF_RESOURCES; + goto fail; + } + + if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || + IS_ENABLED(CONFIG_CMDLINE_FORCE) || + cmdline_size == 0) + efi_parse_options(CONFIG_CMDLINE); + + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) + efi_parse_options(cmdline_ptr); + + pr_efi("Booting Linux Kernel...\n"); + + si = setup_graphics(); + + status = handle_kernel_image(&image_addr, &image_size, + &reserve_addr, + &reserve_size, + dram_base, image); + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to relocate kernel\n"); + goto fail_free_cmdline; + } + + efi_retrieve_tpm2_eventlog(); + + /* Ask the firmware to clear memory on unclean shutdown */ + efi_enable_reset_attack_mitigation(); + + secure_boot = efi_get_secureboot(); + + /* + * Unauthenticated device tree data is a security hazard, so ignore + * 'dtb=' unless UEFI Secure Boot is disabled. We assume that secure + * boot is enabled if we can't determine its state. + */ + if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) || + secure_boot != efi_secureboot_mode_disabled) { + if (strstr(cmdline_ptr, "dtb=")) + pr_efi("Ignoring DTB from command line.\n"); + } else { + status = efi_load_dtb(image, &fdt_addr, &fdt_size); + + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to load device tree!\n"); + goto fail_free_image; + } + } + + if (fdt_addr) { + pr_efi("Using DTB from command line\n"); + } else { + /* Look for a device tree configuration table entry. */ + fdt_addr = (uintptr_t)get_fdt(&fdt_size); + if (fdt_addr) + pr_efi("Using DTB from configuration table\n"); + } + + if (!fdt_addr) + pr_efi("Generating empty DTB\n"); + + if (!noinitrd()) { + max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, + max_addr); + if (status == EFI_SUCCESS) { + pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + } else if (status == EFI_NOT_FOUND) { + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + ULONG_MAX, max_addr); + if (status == EFI_SUCCESS && initrd_size > 0) + pr_efi("Loaded initrd from command line option\n"); + } + if (status != EFI_SUCCESS) + pr_efi_err("Failed to load initrd!\n"); + } + + efi_random_get_seed(); + + /* + * If the NX PE data feature is enabled in the properties table, we + * should take care not to create a virtual mapping that changes the + * relative placement of runtime services code and data regions, as + * they may belong to the same PE/COFF executable image in memory. + * The easiest way to achieve that is to simply use a 1:1 mapping. + */ + prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID); + flat_va_mapping = prop_tbl && + (prop_tbl->memory_protection_attribute & + EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); + + /* hibernation expects the runtime regions to stay in the same place */ + if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) { + /* + * Randomize the base of the UEFI runtime services region. + * Preserve the 2 MB alignment of the region by taking a + * shift of 21 bit positions into account when scaling + * the headroom value using a 32-bit random value. + */ + static const u64 headroom = EFI_RT_VIRTUAL_LIMIT - + EFI_RT_VIRTUAL_BASE - + EFI_RT_VIRTUAL_SIZE; + u32 rnd; + + status = efi_get_random_bytes(sizeof(rnd), (u8 *)&rnd); + if (status == EFI_SUCCESS) { + virtmap_base = EFI_RT_VIRTUAL_BASE + + (((headroom >> 21) * rnd) >> (32 - 21)); + } + } + + install_memreserve_table(); + + status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr, + efi_get_max_fdt_addr(dram_base), + initrd_addr, initrd_size, + cmdline_ptr, fdt_addr, fdt_size); + if (status != EFI_SUCCESS) + goto fail_free_initrd; + + efi_enter_kernel(image_addr, fdt_addr, fdt_totalsize((void *)fdt_addr)); + /* not reached */ + +fail_free_initrd: + pr_efi_err("Failed to update FDT and exit boot services\n"); + + efi_free(initrd_size, initrd_addr); + efi_free(fdt_size, fdt_addr); + +fail_free_image: + efi_free(image_size, image_addr); + efi_free(reserve_size, reserve_addr); +fail_free_cmdline: + free_screen_info(si); + efi_free(cmdline_size, (unsigned long)cmdline_ptr); +fail: + return status; +} + +/* + * efi_get_virtmap() - create a virtual mapping for the EFI memory map + * + * This function populates the virt_addr fields of all memory region descriptors + * in @memory_map whose EFI_MEMORY_RUNTIME attribute is set. Those descriptors + * are also copied to @runtime_map, and their total count is returned in @count. + */ +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + unsigned long desc_size, efi_memory_desc_t *runtime_map, + int *count) +{ + u64 efi_virt_base = virtmap_base; + efi_memory_desc_t *in, *out = runtime_map; + int l; + + for (l = 0; l < map_size; l += desc_size) { + u64 paddr, size; + + in = (void *)memory_map + l; + if (!(in->attribute & EFI_MEMORY_RUNTIME)) + continue; + + paddr = in->phys_addr; + size = in->num_pages * EFI_PAGE_SIZE; + + in->virt_addr = in->phys_addr; + if (novamap()) { + continue; + } + + /* + * Make the mapping compatible with 64k pages: this allows + * a 4k page size kernel to kexec a 64k page size kernel and + * vice versa. + */ + if (!flat_va_mapping) { + + paddr = round_down(in->phys_addr, SZ_64K); + size += in->phys_addr - paddr; + + /* + * Avoid wasting memory on PTEs by choosing a virtual + * base that is compatible with section mappings if this + * region has the appropriate size and physical + * alignment. (Sections are 2 MB on 4k granule kernels) + */ + if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) + efi_virt_base = round_up(efi_virt_base, SZ_2M); + else + efi_virt_base = round_up(efi_virt_base, SZ_64K); + + in->virt_addr += efi_virt_base - paddr; + efi_virt_base += size; + } + + memcpy(out, in, desc_size); + out = (void *)out + desc_size; + ++*count; + } +} -- cgit v1.2.3-70-g09d2 From cf6b83664895a5c7e97710df282e220bd047f0f5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Apr 2020 09:17:35 +0200 Subject: efi/libstub: Make initrd file loader configurable Loading an initrd passed via the kernel command line is deprecated: it is limited to files that reside in the same volume as the one the kernel itself was loaded from, and we have more flexible ways to achieve the same. So make it configurable so new architectures can decide not to enable it. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 11 ++++++++++ drivers/firmware/efi/libstub/efistub.h | 38 ++++++++++++++++++++++++++-------- drivers/firmware/efi/libstub/file.c | 32 +++++++--------------------- 3 files changed, 47 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 2a2b2b96a1dc..4e788dd55b03 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -124,6 +124,17 @@ config EFI_ARMSTUB_DTB_LOADER functionality for bootloaders that do not have such support this option is necessary. +config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER + bool "Enable the command line initrd loader" + depends on EFI_GENERIC_STUB + default y + help + Select this config option to add support for the initrd= command + line parameter, allowing an initrd that resides on the same volume + as the kernel image to be loaded into memory. + + This method is deprecated. + config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" depends on EFI_VARS diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 67d26949fd26..7517683b31e9 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -651,15 +651,35 @@ efi_status_t efi_parse_options(char const *cmdline); efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, unsigned long size); -efi_status_t efi_load_dtb(efi_loaded_image_t *image, - unsigned long *load_addr, - unsigned long *load_size); - -efi_status_t efi_load_initrd(efi_loaded_image_t *image, - unsigned long *load_addr, - unsigned long *load_size, - unsigned long soft_limit, - unsigned long hard_limit); +efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + const efi_char16_t *optstr, + int optstr_size, + unsigned long soft_limit, + unsigned long hard_limit, + unsigned long *load_addr, + unsigned long *load_size); + + +static inline efi_status_t efi_load_dtb(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size) +{ + return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2, + ULONG_MAX, ULONG_MAX, load_addr, load_size); +} + +static inline efi_status_t efi_load_initrd(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long soft_limit, + unsigned long hard_limit) +{ + if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER)) + return EFI_SUCCESS; + + return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, + soft_limit, hard_limit, load_addr, load_size); +} efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, unsigned long *load_size, diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index ea66b1f16a79..27e014ea4459 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -121,13 +121,13 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, * We only support loading a file from the same filesystem as * the kernel image. */ -static efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - const efi_char16_t *optstr, - int optstr_size, - unsigned long soft_limit, - unsigned long hard_limit, - unsigned long *load_addr, - unsigned long *load_size) +efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + const efi_char16_t *optstr, + int optstr_size, + unsigned long soft_limit, + unsigned long hard_limit, + unsigned long *load_addr, + unsigned long *load_size) { const efi_char16_t *cmdline = image->load_options; int cmdline_len = image->load_options_size / 2; @@ -239,21 +239,3 @@ err_close_volume: efi_free(alloc_size, alloc_addr); return status; } - -efi_status_t efi_load_dtb(efi_loaded_image_t *image, - unsigned long *load_addr, - unsigned long *load_size) -{ - return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2, - ULONG_MAX, ULONG_MAX, load_addr, load_size); -} - -efi_status_t efi_load_initrd(efi_loaded_image_t *image, - unsigned long *load_addr, - unsigned long *load_size, - unsigned long soft_limit, - unsigned long hard_limit) -{ - return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, - soft_limit, hard_limit, load_addr, load_size); -} -- cgit v1.2.3-70-g09d2 From 22090f84bc3f8081e0ec180ccaedc85820085376 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Apr 2020 13:44:50 +0200 Subject: efi/libstub: unify EFI call wrappers for non-x86 We have wrappers around EFI calls so that x86 can define special versions for mixed mode, while all other architectures can use the same simple definition that just issues the call directly. In preparation for the arrival of yet another architecture that doesn't need anything special here (RISC-V), let's move the default definition into a shared header. Signed-off-by: Ard Biesheuvel --- arch/arm/include/asm/efi.h | 8 -------- arch/arm64/include/asm/efi.h | 8 -------- drivers/firmware/efi/libstub/efistub.h | 16 ++++++++++++++++ 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h index 5ac46e2860bc..9383f236e795 100644 --- a/arch/arm/include/asm/efi.h +++ b/arch/arm/include/asm/efi.h @@ -50,14 +50,6 @@ void efi_virtmap_unload(void); /* arch specific definitions used by the stub code */ -#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__) -#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__) -#define efi_is_native() (true) - -#define efi_table_attr(inst, attr) (inst->attr) - -#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) - struct screen_info *alloc_screen_info(void); void free_screen_info(struct screen_info *si); diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 45e821222774..d4ab3f73e7a3 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -86,14 +86,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base, return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1)); } -#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__) -#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__) -#define efi_is_native() (true) - -#define efi_table_attr(inst, attr) (inst->attr) - -#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) - #define alloc_screen_info(x...) &screen_info static inline void free_screen_info(struct screen_info *si) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 7517683b31e9..d9ad8582dbea 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -39,6 +39,22 @@ extern bool __pure novamap(void); extern __pure efi_system_table_t *efi_system_table(void); +#ifndef efi_bs_call +#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__) +#endif +#ifndef efi_rt_call +#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__) +#endif +#ifndef efi_is_native +#define efi_is_native() (true) +#endif +#ifndef efi_table_attr +#define efi_table_attr(inst, attr) (inst->attr) +#endif +#ifndef efi_call_proto +#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) +#endif + #define pr_efi(msg) do { \ if (!is_quiet()) efi_printk("EFI stub: "msg); \ } while (0) -- cgit v1.2.3-70-g09d2 From 87cd6378b3d2e0bd3316c3bb005e1d9d4db372fd Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 23 Apr 2020 20:08:33 +0800 Subject: efi/libstub/arm: Make install_memreserve_table static Fix the following sparse warning: drivers/firmware/efi/libstub/arm-stub.c:68:6: warning: symbol 'install_memreserve_table' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: Zou Wei Link: https://lore.kernel.org/r/1587643713-28169-1-git-send-email-zou_wei@huawei.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 99a5cde7c2d8..8a26cc11ca4a 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -65,7 +65,7 @@ static struct screen_info *setup_graphics(void) return si; } -void install_memreserve_table(void) +static void install_memreserve_table(void) { struct linux_efi_memreserve *rsv; efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; -- cgit v1.2.3-70-g09d2 From bd45870409a363eeb67e409645244ce38c7c2df3 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:15 -0400 Subject: efi/gop: Remove redundant current_fb_base current_fb_base isn't used for anything except assigning to fb_base if we locate a suitable gop. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 55e6b3f286fe..f40d535dccb8 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -108,7 +108,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; bool conout_found = false; void *dummy = NULL; - efi_physical_addr_t current_fb_base; status = efi_bs_call(handle_protocol, h, proto, (void **)&gop); if (status != EFI_SUCCESS) @@ -120,7 +119,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, mode = efi_table_attr(gop, mode); info = efi_table_attr(mode, info); - current_fb_base = efi_table_attr(mode, frame_buffer_base); if ((!first_gop || conout_found) && info->pixel_format != PIXEL_BLT_ONLY) { @@ -136,7 +134,7 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, pixel_format = info->pixel_format; pixel_info = info->pixel_information; pixels_per_scan_line = info->pixels_per_scan_line; - fb_base = current_fb_base; + fb_base = efi_table_attr(mode, frame_buffer_base); /* * Once we've found a GOP supporting ConOut, -- cgit v1.2.3-70-g09d2 From 8cd207973c37175bf44727e28aa9c74fcf18de5e Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:16 -0400 Subject: efi/gop: Move check for framebuffer before con_out If the gop doesn't have a framebuffer, there's no point in checking for con_out support. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index f40d535dccb8..201b66970b2b 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -113,15 +113,16 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, if (status != EFI_SUCCESS) continue; + mode = efi_table_attr(gop, mode); + info = efi_table_attr(mode, info); + if (info->pixel_format == PIXEL_BLT_ONLY) + continue; + status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy); if (status == EFI_SUCCESS) conout_found = true; - mode = efi_table_attr(gop, mode); - info = efi_table_attr(mode, info); - - if ((!first_gop || conout_found) && - info->pixel_format != PIXEL_BLT_ONLY) { + if (!first_gop || conout_found) { /* * Systems that use the UEFI Console Splitter may * provide multiple GOP devices, not all of which are -- cgit v1.2.3-70-g09d2 From 6327e6d0e4a52688be621190381eea6a146bb777 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:17 -0400 Subject: efi/gop: Get mode information outside the loop Move extraction of the mode information parameters outside the loop to find the gop, and eliminate some redundant variables. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-4-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 201b66970b2b..d692b8c65813 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -89,12 +89,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, unsigned long size, void **handles) { efi_graphics_output_protocol_t *gop, *first_gop; - u16 width, height; - u32 pixels_per_scan_line; - u32 ext_lfb_base; + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info = NULL; efi_physical_addr_t fb_base; - efi_pixel_bitmask_t pixel_info; - int pixel_format; efi_status_t status; efi_handle_t h; int i; @@ -103,8 +100,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, gop = NULL; for_each_efi_handle(h, handles, size, i) { - efi_graphics_output_protocol_mode_t *mode; - efi_graphics_output_mode_info_t *info = NULL; efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; bool conout_found = false; void *dummy = NULL; @@ -129,15 +124,7 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, * backed by real hardware. The workaround is to search * for a GOP implementing the ConOut protocol, and if * one isn't found, to just fall back to the first GOP. - */ - width = info->horizontal_resolution; - height = info->vertical_resolution; - pixel_format = info->pixel_format; - pixel_info = info->pixel_information; - pixels_per_scan_line = info->pixels_per_scan_line; - fb_base = efi_table_attr(mode, frame_buffer_base); - - /* + * * Once we've found a GOP supporting ConOut, * don't bother looking any further. */ @@ -152,21 +139,24 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, return EFI_NOT_FOUND; /* EFI framebuffer */ + mode = efi_table_attr(first_gop, mode); + info = efi_table_attr(mode, info); + si->orig_video_isVGA = VIDEO_TYPE_EFI; - si->lfb_width = width; - si->lfb_height = height; - si->lfb_base = fb_base; + si->lfb_width = info->horizontal_resolution; + si->lfb_height = info->vertical_resolution; - ext_lfb_base = (u64)(unsigned long)fb_base >> 32; - if (ext_lfb_base) { + fb_base = efi_table_attr(mode, frame_buffer_base); + si->lfb_base = fb_base; + si->ext_lfb_base = (u64)(unsigned long)fb_base >> 32; + if (si->ext_lfb_base) si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; - si->ext_lfb_base = ext_lfb_base; - } si->pages = 1; - setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format); + setup_pixel_info(si, info->pixels_per_scan_line, + info->pixel_information, info->pixel_format); si->lfb_size = si->lfb_linelength * si->lfb_height; -- cgit v1.2.3-70-g09d2 From ecf53091f34af7172322ed40cfb5acf487329561 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:18 -0400 Subject: efi/gop: Factor out locating the gop into a function Move the loop to find a gop into its own function. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-5-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index d692b8c65813..92abcf558845 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -85,19 +85,17 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, } } -static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, - unsigned long size, void **handles) +static efi_graphics_output_protocol_t * +find_gop(efi_guid_t *proto, unsigned long size, void **handles) { efi_graphics_output_protocol_t *gop, *first_gop; efi_graphics_output_protocol_mode_t *mode; efi_graphics_output_mode_info_t *info = NULL; - efi_physical_addr_t fb_base; efi_status_t status; efi_handle_t h; int i; first_gop = NULL; - gop = NULL; for_each_efi_handle(h, handles, size, i) { efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; @@ -134,12 +132,25 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, } } + return first_gop; +} + +static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, + unsigned long size, void **handles) +{ + efi_graphics_output_protocol_t *gop; + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info = NULL; + efi_physical_addr_t fb_base; + + gop = find_gop(proto, size, handles); + /* Did we find any GOPs? */ - if (!first_gop) + if (!gop) return EFI_NOT_FOUND; /* EFI framebuffer */ - mode = efi_table_attr(first_gop, mode); + mode = efi_table_attr(gop, mode); info = efi_table_attr(mode, info); si->orig_video_isVGA = VIDEO_TYPE_EFI; -- cgit v1.2.3-70-g09d2 From 8e0a22e2b0531870e61110d12b1d37d6b5d24eed Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:19 -0400 Subject: efi/gop: Slightly re-arrange logic of find_gop Small cleanup to get rid of conout_found. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-6-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 92abcf558845..a7d3efe36c78 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -99,7 +99,6 @@ find_gop(efi_guid_t *proto, unsigned long size, void **handles) for_each_efi_handle(h, handles, size, i) { efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; - bool conout_found = false; void *dummy = NULL; status = efi_bs_call(handle_protocol, h, proto, (void **)&gop); @@ -111,25 +110,22 @@ find_gop(efi_guid_t *proto, unsigned long size, void **handles) if (info->pixel_format == PIXEL_BLT_ONLY) continue; + /* + * Systems that use the UEFI Console Splitter may + * provide multiple GOP devices, not all of which are + * backed by real hardware. The workaround is to search + * for a GOP implementing the ConOut protocol, and if + * one isn't found, to just fall back to the first GOP. + * + * Once we've found a GOP supporting ConOut, + * don't bother looking any further. + */ status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy); if (status == EFI_SUCCESS) - conout_found = true; - - if (!first_gop || conout_found) { - /* - * Systems that use the UEFI Console Splitter may - * provide multiple GOP devices, not all of which are - * backed by real hardware. The workaround is to search - * for a GOP implementing the ConOut protocol, and if - * one isn't found, to just fall back to the first GOP. - * - * Once we've found a GOP supporting ConOut, - * don't bother looking any further. - */ + return gop; + + if (!first_gop) first_gop = gop; - if (conout_found) - break; - } } return first_gop; -- cgit v1.2.3-70-g09d2 From e484c594ba0e34686ad8780287961d09a3b4169b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:20 -0400 Subject: efi/gop: Move variable declarations into loop block Declare the variables inside the block where they're used. Get rid of a couple of redundant initializers. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-7-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index a7d3efe36c78..0d195060a370 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -88,16 +88,19 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, static efi_graphics_output_protocol_t * find_gop(efi_guid_t *proto, unsigned long size, void **handles) { - efi_graphics_output_protocol_t *gop, *first_gop; - efi_graphics_output_protocol_mode_t *mode; - efi_graphics_output_mode_info_t *info = NULL; - efi_status_t status; + efi_graphics_output_protocol_t *first_gop; efi_handle_t h; int i; first_gop = NULL; for_each_efi_handle(h, handles, size, i) { + efi_status_t status; + + efi_graphics_output_protocol_t *gop; + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info; + efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; void *dummy = NULL; @@ -136,7 +139,7 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, { efi_graphics_output_protocol_t *gop; efi_graphics_output_protocol_mode_t *mode; - efi_graphics_output_mode_info_t *info = NULL; + efi_graphics_output_mode_info_t *info; efi_physical_addr_t fb_base; gop = find_gop(proto, size, handles); -- cgit v1.2.3-70-g09d2 From f1d1853bdbcfb2d00ae0b850baf26d87e6d363d8 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:21 -0400 Subject: efi/gop: Use helper macros for populating lfb_base Use the lower/upper_32_bits macros from kernel.h to initialize si->lfb_base and si->ext_lfb_base. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-8-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 0d195060a370..7b0baf9a912f 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -158,8 +158,8 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, si->lfb_height = info->vertical_resolution; fb_base = efi_table_attr(mode, frame_buffer_base); - si->lfb_base = fb_base; - si->ext_lfb_base = (u64)(unsigned long)fb_base >> 32; + si->lfb_base = lower_32_bits(fb_base); + si->ext_lfb_base = upper_32_bits(fb_base); if (si->ext_lfb_base) si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; -- cgit v1.2.3-70-g09d2 From 9867fc9de6a6a7a54edb2c43540c6db226e84a14 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:22 -0400 Subject: efi/gop: Use helper macros for find_bits Use the __ffs/__fls macros to calculate the position and size of the mask. Correct type of mask to u32 instead of unsigned long. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-9-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 7b0baf9a912f..8bf424f35759 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -5,6 +5,7 @@ * * ----------------------------------------------------------------------- */ +#include #include #include #include @@ -12,27 +13,16 @@ #include "efistub.h" -static void find_bits(unsigned long mask, u8 *pos, u8 *size) +static void find_bits(u32 mask, u8 *pos, u8 *size) { - u8 first, len; - - first = 0; - len = 0; - - if (mask) { - while (!(mask & 0x1)) { - mask = mask >> 1; - first++; - } - - while (mask & 0x1) { - mask = mask >> 1; - len++; - } + if (!mask) { + *pos = *size = 0; + return; } - *pos = first; - *size = len; + /* UEFI spec guarantees that the set bits are contiguous */ + *pos = __ffs(mask); + *size = __fls(mask) - *pos + 1; } static void -- cgit v1.2.3-70-g09d2 From d49fd4bbf9bb0dbee3a3eed301ffeeb75636053b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:23 -0400 Subject: efi/gop: Remove unreachable code from setup_pixel_info pixel_format must be one of PIXEL_RGB_RESERVED_8BIT_PER_COLOR PIXEL_BGR_RESERVED_8BIT_PER_COLOR PIXEL_BIT_MASK since we skip PIXEL_BLT_ONLY when finding a gop. Remove the redundant code and add another check in find_gop to skip any pixel formats that we don't know about, in case a later version of the UEFI spec adds one. Reformat the code a little. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-10-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 66 +++++++++++++++----------------------- 1 file changed, 26 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 8bf424f35759..2d91699e3061 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -29,49 +29,34 @@ static void setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, efi_pixel_bitmask_t pixel_info, int pixel_format) { - if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { - si->lfb_depth = 32; - si->lfb_linelength = pixels_per_scan_line * 4; - si->red_size = 8; - si->red_pos = 0; - si->green_size = 8; - si->green_pos = 8; - si->blue_size = 8; - si->blue_pos = 16; - si->rsvd_size = 8; - si->rsvd_pos = 24; - } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) { - si->lfb_depth = 32; - si->lfb_linelength = pixels_per_scan_line * 4; - si->red_size = 8; - si->red_pos = 16; - si->green_size = 8; - si->green_pos = 8; - si->blue_size = 8; - si->blue_pos = 0; - si->rsvd_size = 8; - si->rsvd_pos = 24; - } else if (pixel_format == PIXEL_BIT_MASK) { - find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size); - find_bits(pixel_info.green_mask, &si->green_pos, - &si->green_size); - find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size); - find_bits(pixel_info.reserved_mask, &si->rsvd_pos, - &si->rsvd_size); + if (pixel_format == PIXEL_BIT_MASK) { + find_bits(pixel_info.red_mask, + &si->red_pos, &si->red_size); + find_bits(pixel_info.green_mask, + &si->green_pos, &si->green_size); + find_bits(pixel_info.blue_mask, + &si->blue_pos, &si->blue_size); + find_bits(pixel_info.reserved_mask, + &si->rsvd_pos, &si->rsvd_size); si->lfb_depth = si->red_size + si->green_size + si->blue_size + si->rsvd_size; si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; } else { - si->lfb_depth = 4; - si->lfb_linelength = si->lfb_width / 2; - si->red_size = 0; - si->red_pos = 0; - si->green_size = 0; - si->green_pos = 0; - si->blue_size = 0; - si->blue_pos = 0; - si->rsvd_size = 0; - si->rsvd_pos = 0; + if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { + si->red_pos = 0; + si->blue_pos = 16; + } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ { + si->blue_pos = 0; + si->red_pos = 16; + } + + si->green_pos = 8; + si->rsvd_pos = 24; + si->red_size = si->green_size = + si->blue_size = si->rsvd_size = 8; + + si->lfb_depth = 32; + si->lfb_linelength = pixels_per_scan_line * 4; } } @@ -100,7 +85,8 @@ find_gop(efi_guid_t *proto, unsigned long size, void **handles) mode = efi_table_attr(gop, mode); info = efi_table_attr(mode, info); - if (info->pixel_format == PIXEL_BLT_ONLY) + if (info->pixel_format == PIXEL_BLT_ONLY || + info->pixel_format >= PIXEL_FORMAT_MAX) continue; /* -- cgit v1.2.3-70-g09d2 From b4b89a02724245c4f4eda9dbfb7895ddf122ca7f Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:24 -0400 Subject: efi/gop: Add prototypes for query_mode and set_mode Add prototypes and argmap for the Graphics Output Protocol's QueryMode and SetMode functions. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-11-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 4 ++++ drivers/firmware/efi/libstub/efistub.h | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 8391c115c0ec..f59cba117dcb 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -307,6 +307,10 @@ static inline u32 efi64_convert_status(efi_status_t status) #define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \ ((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf)) +/* Graphics Output Protocol */ +#define __efi64_argmap_query_mode(gop, mode, size, info) \ + ((gop), (mode), efi64_zero_upper(size), efi64_zero_upper(info)) + /* * The macros below handle the plumbing for the argument mapping. To add a * mapping for a specific EFI method, simply define a macro diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index d9ad8582dbea..321244ed20a4 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -314,8 +314,10 @@ typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t; union efi_graphics_output_protocol { struct { - void *query_mode; - void *set_mode; + efi_status_t (__efiapi *query_mode)(efi_graphics_output_protocol_t *, + u32, unsigned long *, + efi_graphics_output_mode_info_t **); + efi_status_t (__efiapi *set_mode) (efi_graphics_output_protocol_t *, u32); void *blt; efi_graphics_output_protocol_mode_t *mode; }; -- cgit v1.2.3-70-g09d2 From fffb68047e563fb74f782c726e9bdf1fa117d93d Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:25 -0400 Subject: efi/gop: Allow specifying mode number on command line Add the ability to choose a video mode for the selected gop by using a command-line argument of the form video=efifb:mode= Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-12-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/fb/efifb.rst | 20 ++++- drivers/firmware/efi/libstub/efi-stub-helper.c | 3 + drivers/firmware/efi/libstub/efistub.h | 2 + drivers/firmware/efi/libstub/gop.c | 107 +++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/Documentation/fb/efifb.rst b/Documentation/fb/efifb.rst index 04840331a00e..367fbda2f4da 100644 --- a/Documentation/fb/efifb.rst +++ b/Documentation/fb/efifb.rst @@ -2,8 +2,10 @@ What is efifb? ============== -This is a generic EFI platform driver for Intel based Apple computers. -efifb is only for EFI booted Intel Macs. +This is a generic EFI platform driver for systems with UEFI firmware. The +system must be booted via the EFI stub for this to be usable. efifb supports +both firmware with Graphics Output Protocol (GOP) displays as well as older +systems with only Universal Graphics Adapter (UGA) displays. Supported Hardware ================== @@ -12,11 +14,14 @@ Supported Hardware - Macbook - Macbook Pro 15"/17" - MacMini +- ARM/ARM64/X86 systems with UEFI firmware How to use it? ============== -efifb does not have any kind of autodetection of your machine. +For UGA displays, efifb does not have any kind of autodetection of your +machine. + You have to add the following kernel parameters in your elilo.conf:: Macbook : @@ -28,6 +33,9 @@ You have to add the following kernel parameters in your elilo.conf:: Macbook Pro 17", iMac 20" : video=efifb:i20 +For GOP displays, efifb can autodetect the display's resolution and framebuffer +address, so these should work out of the box without any special parameters. + Accepted options: ======= =========================================================== @@ -36,4 +44,10 @@ nowc Don't map the framebuffer write combined. This can be used when large amounts of console data are written. ======= =========================================================== +Options for GOP displays: + +mode=n + The EFI stub will set the mode of the display to mode number n if + possible. + Edgar Hucek diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 9f34c7242939..c6092b6038cf 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -105,6 +105,9 @@ efi_status_t efi_parse_options(char const *cmdline) efi_disable_pci_dma = true; if (parse_option_str(val, "no_disable_early_pci_dma")) efi_disable_pci_dma = false; + } else if (!strcmp(param, "video") && + val && strstarts(val, "efifb:")) { + efi_parse_option_graphics(val + strlen("efifb:")); } } efi_bs_call(free_pool, buf); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 321244ed20a4..9af65be3b278 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -666,6 +666,8 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, efi_status_t efi_parse_options(char const *cmdline); +void efi_parse_option_graphics(char *option); + efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, unsigned long size); diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 2d91699e3061..a32b784b4577 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -8,11 +8,115 @@ #include #include #include +#include #include #include #include "efistub.h" +enum efi_cmdline_option { + EFI_CMDLINE_NONE, + EFI_CMDLINE_MODE_NUM, +}; + +static struct { + enum efi_cmdline_option option; + u32 mode; +} cmdline __efistub_global = { .option = EFI_CMDLINE_NONE }; + +static bool parse_modenum(char *option, char **next) +{ + u32 m; + + if (!strstarts(option, "mode=")) + return false; + option += strlen("mode="); + m = simple_strtoull(option, &option, 0); + if (*option && *option++ != ',') + return false; + cmdline.option = EFI_CMDLINE_MODE_NUM; + cmdline.mode = m; + + *next = option; + return true; +} + +void efi_parse_option_graphics(char *option) +{ + while (*option) { + if (parse_modenum(option, &option)) + continue; + + while (*option && *option++ != ',') + ; + } +} + +static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) +{ + efi_status_t status; + + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info; + unsigned long info_size; + + u32 max_mode, cur_mode; + int pf; + + mode = efi_table_attr(gop, mode); + + cur_mode = efi_table_attr(mode, mode); + if (cmdline.mode == cur_mode) + return cur_mode; + + max_mode = efi_table_attr(mode, max_mode); + if (cmdline.mode >= max_mode) { + efi_printk("Requested mode is invalid\n"); + return cur_mode; + } + + status = efi_call_proto(gop, query_mode, cmdline.mode, + &info_size, &info); + if (status != EFI_SUCCESS) { + efi_printk("Couldn't get mode information\n"); + return cur_mode; + } + + pf = info->pixel_format; + + efi_bs_call(free_pool, info); + + if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { + efi_printk("Invalid PixelFormat\n"); + return cur_mode; + } + + return cmdline.mode; +} + +static void set_mode(efi_graphics_output_protocol_t *gop) +{ + efi_graphics_output_protocol_mode_t *mode; + u32 cur_mode, new_mode; + + switch (cmdline.option) { + case EFI_CMDLINE_MODE_NUM: + new_mode = choose_mode_modenum(gop); + break; + default: + return; + } + + mode = efi_table_attr(gop, mode); + cur_mode = efi_table_attr(mode, mode); + + if (new_mode == cur_mode) + return; + + if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) + efi_printk("Failed to set requested mode\n"); +} + static void find_bits(u32 mask, u8 *pos, u8 *size) { if (!mask) { @@ -124,6 +228,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, if (!gop) return EFI_NOT_FOUND; + /* Change mode if requested */ + set_mode(gop); + /* EFI framebuffer */ mode = efi_table_attr(gop, mode); info = efi_table_attr(mode, info); -- cgit v1.2.3-70-g09d2 From d9ff0323d074c6c06467118c7a43d5748f147369 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:26 -0400 Subject: efi/gop: Allow specifying mode by x Add the ability to choose a video mode using a command-line argument of the form video=efifb:x Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-13-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/fb/efifb.rst | 5 +++ drivers/firmware/efi/libstub/gop.c | 84 +++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/fb/efifb.rst b/Documentation/fb/efifb.rst index 367fbda2f4da..635275071307 100644 --- a/Documentation/fb/efifb.rst +++ b/Documentation/fb/efifb.rst @@ -50,4 +50,9 @@ mode=n The EFI stub will set the mode of the display to mode number n if possible. +x + The EFI stub will search for a display mode that matches the specified + horizontal and vertical resolution, and set the mode of the display to + it if one is found. + Edgar Hucek diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index a32b784b4577..cc84e6a82f54 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -6,6 +6,7 @@ * ----------------------------------------------------------------------- */ #include +#include #include #include #include @@ -17,11 +18,17 @@ enum efi_cmdline_option { EFI_CMDLINE_NONE, EFI_CMDLINE_MODE_NUM, + EFI_CMDLINE_RES }; static struct { enum efi_cmdline_option option; - u32 mode; + union { + u32 mode; + struct { + u32 width, height; + } res; + }; } cmdline __efistub_global = { .option = EFI_CMDLINE_NONE }; static bool parse_modenum(char *option, char **next) @@ -41,11 +48,33 @@ static bool parse_modenum(char *option, char **next) return true; } +static bool parse_res(char *option, char **next) +{ + u32 w, h; + + if (!isdigit(*option)) + return false; + w = simple_strtoull(option, &option, 10); + if (*option++ != 'x' || !isdigit(*option)) + return false; + h = simple_strtoull(option, &option, 10); + if (*option && *option++ != ',') + return false; + cmdline.option = EFI_CMDLINE_RES; + cmdline.res.width = w; + cmdline.res.height = h; + + *next = option; + return true; +} + void efi_parse_option_graphics(char *option) { while (*option) { if (parse_modenum(option, &option)) continue; + if (parse_res(option, &option)) + continue; while (*option && *option++ != ',') ; @@ -94,6 +123,56 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) return cmdline.mode; } +static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) +{ + efi_status_t status; + + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info; + unsigned long info_size; + + u32 max_mode, cur_mode; + int pf; + u32 m, w, h; + + mode = efi_table_attr(gop, mode); + + cur_mode = efi_table_attr(mode, mode); + info = efi_table_attr(mode, info); + w = info->horizontal_resolution; + h = info->vertical_resolution; + + if (w == cmdline.res.width && h == cmdline.res.height) + return cur_mode; + + max_mode = efi_table_attr(mode, max_mode); + + for (m = 0; m < max_mode; m++) { + if (m == cur_mode) + continue; + + status = efi_call_proto(gop, query_mode, m, + &info_size, &info); + if (status != EFI_SUCCESS) + continue; + + pf = info->pixel_format; + w = info->horizontal_resolution; + h = info->vertical_resolution; + + efi_bs_call(free_pool, info); + + if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) + continue; + if (w == cmdline.res.width && h == cmdline.res.height) + return m; + } + + efi_printk("Couldn't find requested mode\n"); + + return cur_mode; +} + static void set_mode(efi_graphics_output_protocol_t *gop) { efi_graphics_output_protocol_mode_t *mode; @@ -103,6 +182,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop) case EFI_CMDLINE_MODE_NUM: new_mode = choose_mode_modenum(gop); break; + case EFI_CMDLINE_RES: + new_mode = choose_mode_res(gop); + break; default: return; } -- cgit v1.2.3-70-g09d2 From 9a1663bc4d9856f6810786fec597dab5440a9d8d Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 19 Mar 2020 22:00:27 -0400 Subject: efi/gop: Allow specifying depth as well as resolution Extend the video mode argument to handle an optional color depth specification of the form video=efifb:x[-(rgb|bgr|)] Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200320020028.1936003-14-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/fb/efifb.rst | 8 ++++--- drivers/firmware/efi/libstub/gop.c | 48 ++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/Documentation/fb/efifb.rst b/Documentation/fb/efifb.rst index 635275071307..eca38466487a 100644 --- a/Documentation/fb/efifb.rst +++ b/Documentation/fb/efifb.rst @@ -50,9 +50,11 @@ mode=n The EFI stub will set the mode of the display to mode number n if possible. -x +x[-(rgb|bgr|)] The EFI stub will search for a display mode that matches the specified - horizontal and vertical resolution, and set the mode of the display to - it if one is found. + horizontal and vertical resolution, and optionally bit depth, and set + the mode of the display to it if one is found. The bit depth can either + "rgb" or "bgr" to match specifically those pixel formats, or a number + for a mode with matching bits per pixel. Edgar Hucek diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index cc84e6a82f54..848cb605a9c4 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -27,6 +27,8 @@ static struct { u32 mode; struct { u32 width, height; + int format; + u8 depth; } res; }; } cmdline __efistub_global = { .option = EFI_CMDLINE_NONE }; @@ -50,7 +52,8 @@ static bool parse_modenum(char *option, char **next) static bool parse_res(char *option, char **next) { - u32 w, h; + u32 w, h, d = 0; + int pf = -1; if (!isdigit(*option)) return false; @@ -58,11 +61,26 @@ static bool parse_res(char *option, char **next) if (*option++ != 'x' || !isdigit(*option)) return false; h = simple_strtoull(option, &option, 10); + if (*option == '-') { + option++; + if (strstarts(option, "rgb")) { + option += strlen("rgb"); + pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR; + } else if (strstarts(option, "bgr")) { + option += strlen("bgr"); + pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR; + } else if (isdigit(*option)) + d = simple_strtoull(option, &option, 10); + else + return false; + } if (*option && *option++ != ',') return false; cmdline.option = EFI_CMDLINE_RES; cmdline.res.width = w; cmdline.res.height = h; + cmdline.res.format = pf; + cmdline.res.depth = d; *next = option; return true; @@ -123,6 +141,18 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) return cmdline.mode; } +static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info) +{ + if (pixel_format == PIXEL_BIT_MASK) { + u32 mask = pixel_info.red_mask | pixel_info.green_mask | + pixel_info.blue_mask | pixel_info.reserved_mask; + if (!mask) + return 0; + return __fls(mask) - __ffs(mask) + 1; + } else + return 32; +} + static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) { efi_status_t status; @@ -133,16 +163,21 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) u32 max_mode, cur_mode; int pf; + efi_pixel_bitmask_t pi; u32 m, w, h; mode = efi_table_attr(gop, mode); cur_mode = efi_table_attr(mode, mode); info = efi_table_attr(mode, info); - w = info->horizontal_resolution; - h = info->vertical_resolution; + pf = info->pixel_format; + pi = info->pixel_information; + w = info->horizontal_resolution; + h = info->vertical_resolution; - if (w == cmdline.res.width && h == cmdline.res.height) + if (w == cmdline.res.width && h == cmdline.res.height && + (cmdline.res.format < 0 || cmdline.res.format == pf) && + (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) return cur_mode; max_mode = efi_table_attr(mode, max_mode); @@ -157,6 +192,7 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) continue; pf = info->pixel_format; + pi = info->pixel_information; w = info->horizontal_resolution; h = info->vertical_resolution; @@ -164,7 +200,9 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) continue; - if (w == cmdline.res.width && h == cmdline.res.height) + if (w == cmdline.res.width && h == cmdline.res.height && + (cmdline.res.format < 0 || cmdline.res.format == pf) && + (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) return m; } -- cgit v1.2.3-70-g09d2 From 45d97a749e9fec6d5324b19561ce5fbfa937d60b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sat, 28 Mar 2020 12:06:01 -0400 Subject: efi/gop: Allow automatically choosing the best mode Add the ability to automatically pick the highest resolution video mode (defined as the product of vertical and horizontal resolution) by using a command-line argument of the form video=efifb:auto If there are multiple modes with the highest resolution, pick one with the highest color depth. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200328160601.378299-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/fb/efifb.rst | 6 +++ drivers/firmware/efi/libstub/gop.c | 84 +++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/fb/efifb.rst b/Documentation/fb/efifb.rst index eca38466487a..519550517fd4 100644 --- a/Documentation/fb/efifb.rst +++ b/Documentation/fb/efifb.rst @@ -57,4 +57,10 @@ mode=n "rgb" or "bgr" to match specifically those pixel formats, or a number for a mode with matching bits per pixel. +auto + The EFI stub will choose the mode with the highest resolution (product + of horizontal and vertical resolution). If there are multiple modes + with the highest resolution, it will choose one with the highest color + depth. + Edgar Hucek diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 848cb605a9c4..fa05a0b0adfd 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -18,7 +18,8 @@ enum efi_cmdline_option { EFI_CMDLINE_NONE, EFI_CMDLINE_MODE_NUM, - EFI_CMDLINE_RES + EFI_CMDLINE_RES, + EFI_CMDLINE_AUTO }; static struct { @@ -86,6 +87,19 @@ static bool parse_res(char *option, char **next) return true; } +static bool parse_auto(char *option, char **next) +{ + if (!strstarts(option, "auto")) + return false; + option += strlen("auto"); + if (*option && *option++ != ',') + return false; + cmdline.option = EFI_CMDLINE_AUTO; + + *next = option; + return true; +} + void efi_parse_option_graphics(char *option) { while (*option) { @@ -93,6 +107,8 @@ void efi_parse_option_graphics(char *option) continue; if (parse_res(option, &option)) continue; + if (parse_auto(option, &option)) + continue; while (*option && *option++ != ',') ; @@ -211,6 +227,69 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) return cur_mode; } +static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop) +{ + efi_status_t status; + + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info; + unsigned long info_size; + + u32 max_mode, cur_mode, best_mode, area; + u8 depth; + int pf; + efi_pixel_bitmask_t pi; + u32 m, w, h, a; + u8 d; + + mode = efi_table_attr(gop, mode); + + cur_mode = efi_table_attr(mode, mode); + max_mode = efi_table_attr(mode, max_mode); + + info = efi_table_attr(mode, info); + + pf = info->pixel_format; + pi = info->pixel_information; + w = info->horizontal_resolution; + h = info->vertical_resolution; + + best_mode = cur_mode; + area = w * h; + depth = pixel_bpp(pf, pi); + + for (m = 0; m < max_mode; m++) { + if (m == cur_mode) + continue; + + status = efi_call_proto(gop, query_mode, m, + &info_size, &info); + if (status != EFI_SUCCESS) + continue; + + pf = info->pixel_format; + pi = info->pixel_information; + w = info->horizontal_resolution; + h = info->vertical_resolution; + + efi_bs_call(free_pool, info); + + if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) + continue; + a = w * h; + if (a < area) + continue; + d = pixel_bpp(pf, pi); + if (a > area || d > depth) { + best_mode = m; + area = a; + depth = d; + } + } + + return best_mode; +} + static void set_mode(efi_graphics_output_protocol_t *gop) { efi_graphics_output_protocol_mode_t *mode; @@ -223,6 +302,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop) case EFI_CMDLINE_RES: new_mode = choose_mode_res(gop); break; + case EFI_CMDLINE_AUTO: + new_mode = choose_mode_auto(gop); + break; default: return; } -- cgit v1.2.3-70-g09d2 From e1df73e2d18b3b7d66f2ec38d81d9566b3a7fb21 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 13 Apr 2020 16:01:05 +0200 Subject: efi/libstub/random: Align allocate size to EFI_ALLOC_ALIGN The EFI stub uses a per-architecture #define for the minimum base and size alignment of page allocations, which is set to 4 KB for all architecures except arm64, which uses 64 KB, to ensure that allocations can always be (un)mapped efficiently, regardless of the page size used by the kernel proper, which could be a kexec'ee The API wrappers around page based allocations assume that this alignment is always taken into account, and so efi_free() will also round up its size argument to EFI_ALLOC_ALIGN. Currently, efi_random_alloc() does not honour this alignment for the allocated size, and so freeing such an allocation may result in unrelated memory to be freed, potentially leading to issues after boot. So let's round up size in efi_random_alloc() as well. Fixes: 2ddbfc81eac84a29 ("efi: stub: add implementation of efi_random_alloc()") Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/randomalloc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 4578f59e160c..6200dfa650f5 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -74,6 +74,8 @@ efi_status_t efi_random_alloc(unsigned long size, if (align < EFI_ALLOC_ALIGN) align = EFI_ALLOC_ALIGN; + size = round_up(size, EFI_ALLOC_ALIGN); + /* count the suitable slots in each memory map entry */ for (map_offset = 0; map_offset < map_size; map_offset += desc_size) { efi_memory_desc_t *md = (void *)memory_map + map_offset; @@ -109,7 +111,7 @@ efi_status_t efi_random_alloc(unsigned long size, } target = round_up(md->phys_addr, align) + target_slot * align; - pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; + pages = size / EFI_PAGE_SIZE; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, pages, &target); -- cgit v1.2.3-70-g09d2 From c37c9162a2fae026dc8570ad7c5ade0422b0a267 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 13 Apr 2020 16:06:56 +0200 Subject: efi/libstub/random: Increase random alloc granularity The implementation of efi_random_alloc() arbitrarily truncates the provided random seed to 16 bits, which limits the granularity of the randomly chosen allocation offset in memory. This is currently only an issue if the size of physical memory exceeds 128 GB, but going forward, we will reduce the allocation alignment to 64 KB, and this means we need to increase the granularity to ensure that the random memory allocations are distributed evenly. We will need to switch to 64-bit arithmetic for the multiplication, but this does not result in 64-bit integer intrinsic calls on ARM or on i386. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/randomalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 6200dfa650f5..a408df474d83 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -87,7 +87,7 @@ efi_status_t efi_random_alloc(unsigned long size, } /* find a random number between 0 and total_slots */ - target_slot = (total_slots * (u16)random_seed) >> 16; + target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32; /* * target_slot is now a value in the range [0, total_slots), and so -- cgit v1.2.3-70-g09d2 From 82046702e288bdd81673b81ab5138e498d155918 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 27 Mar 2020 17:23:52 +0100 Subject: efi/libstub/arm64: Replace 'preferred' offset with alignment check The notion of a 'preferred' load offset for the kernel dates back to the times when the kernel's primary mapping overlapped with the linear region, and memory below it could not be used at all. Today, the arm64 kernel does not really care where it is loaded in physical memory, as long as the alignment requirements are met, and so there is no point in unconditionally moving the kernel to a new location in memory at boot. Instead, we can - check for a KASLR seed, and randomly reallocate the kernel if one is provided - otherwise, check whether the alignment requirements are met for the current placement of the kernel, and just run it in place if they are - finally, do an ordinary page allocation and reallocate the kernel to a suitably aligned buffer anywhere in memory. By the same reasoning, there is no need to take TEXT_OFFSET into account if it is a round multiple of the minimum alignment, which is the usual case for relocatable kernels with TEXT_OFFSET randomization disabled. Otherwise, it suffices to use the relative misaligment of TEXT_OFFSET when reallocating the kernel. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm64-stub.c | 62 +++++++++++++------------------ 1 file changed, 25 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index fc9f8ab533a7..cfd535c13242 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -34,6 +34,15 @@ efi_status_t check_platform_features(void) return EFI_SUCCESS; } +/* + * Relocatable kernels can fix up the misalignment with respect to + * MIN_KIMG_ALIGN, so they only require a minimum alignment of EFI_KIMG_ALIGN + * (which accounts for the alignment of statically allocated objects such as + * the swapper stack.) + */ +static const u64 min_kimg_align = IS_ENABLED(CONFIG_RELOCATABLE) ? EFI_KIMG_ALIGN + : MIN_KIMG_ALIGN; + efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, @@ -43,7 +52,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, { efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; - unsigned long preferred_offset; u64 phys_seed = 0; if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { @@ -61,14 +69,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } } - /* - * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond - * a 2 MB aligned base, which itself may be lower than dram_base, as - * long as the resulting offset equals or exceeds it. - */ - preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET; - if (preferred_offset < dram_base) - preferred_offset += MIN_KIMG_ALIGN; + if (image->image_base != _text) + pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); kernel_size = _edata - _text; kernel_memsize = kernel_size + (_end - _edata); @@ -103,46 +105,32 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, *image_addr = *reserve_addr + offset; } else { - /* - * Else, try a straight allocation at the preferred offset. - * This will work around the issue where, if dram_base == 0x0, - * efi_low_alloc() refuses to allocate at 0x0 (to prevent the - * address of the allocation to be mistaken for a FAIL return - * value or a NULL pointer). It will also ensure that, on - * platforms where the [dram_base, dram_base + TEXT_OFFSET) - * interval is partially occupied by the firmware (like on APM - * Mustang), we can still place the kernel at the address - * 'dram_base + TEXT_OFFSET'. - */ - *image_addr = (unsigned long)_text; - if (*image_addr == preferred_offset) - return EFI_SUCCESS; - - *image_addr = *reserve_addr = preferred_offset; - *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN); - - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, - *reserve_size / EFI_PAGE_SIZE, - (efi_physical_addr_t *)reserve_addr); + status = EFI_OUT_OF_RESOURCES; } if (status != EFI_SUCCESS) { - *reserve_size = kernel_memsize + TEXT_OFFSET; + if (IS_ALIGNED((u64)_text - TEXT_OFFSET, min_kimg_align)) { + /* + * Just execute from wherever we were loaded by the + * UEFI PE/COFF loader if the alignment is suitable. + */ + *image_addr = (u64)_text; + *reserve_size = 0; + return EFI_SUCCESS; + } + + *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align; status = efi_low_alloc(*reserve_size, - MIN_KIMG_ALIGN, reserve_addr); + min_kimg_align, reserve_addr); if (status != EFI_SUCCESS) { pr_efi_err("Failed to relocate kernel\n"); *reserve_size = 0; return status; } - *image_addr = *reserve_addr + TEXT_OFFSET; + *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align; } - if (image->image_base != _text) - pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); - memcpy((void *)*image_addr, _text, kernel_size); return EFI_SUCCESS; -- cgit v1.2.3-70-g09d2 From 5d12da9dd6c155f7a09dfcaa2b9827764c939b2a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 13 Apr 2020 15:36:37 +0200 Subject: efi/libstub/arm64: Simplify randomized loading of kernel image The KASLR code path in the arm64 version of the EFI stub incorporates some overly complicated logic to randomly allocate a region of the right alignment: there is no need to randomize the placement of the kernel modulo 2 MiB separately from the placement of the 2 MiB aligned allocation itself - we can simply follow the same logic used by the non-randomized placement, which is to allocate at the correct alignment, and only take TEXT_OFFSET into account if it is not a round multiple of the alignment. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm64-stub.c | 32 +++++-------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index cfd535c13242..6fc3bd9a56db 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -52,7 +52,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, { efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; - u64 phys_seed = 0; + u32 phys_seed = 0; if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { if (!nokaslr()) { @@ -74,36 +74,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, kernel_size = _edata - _text; kernel_memsize = kernel_size + (_end - _edata); + *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align; if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { - /* - * Produce a displacement in the interval [0, MIN_KIMG_ALIGN) - * that doesn't violate this kernel's de-facto alignment - * constraints. - */ - u32 mask = (MIN_KIMG_ALIGN - 1) & ~(EFI_KIMG_ALIGN - 1); - u32 offset = (phys_seed >> 32) & mask; - - /* - * With CONFIG_RANDOMIZE_TEXT_OFFSET=y, TEXT_OFFSET may not - * be a multiple of EFI_KIMG_ALIGN, and we must ensure that - * we preserve the misalignment of 'offset' relative to - * EFI_KIMG_ALIGN so that statically allocated objects whose - * alignment exceeds PAGE_SIZE appear correctly aligned in - * memory. - */ - offset |= TEXT_OFFSET % EFI_KIMG_ALIGN; - /* * If KASLR is enabled, and we have some randomness available, * locate the kernel at a randomized offset in physical memory. */ - *reserve_size = kernel_memsize + offset; - status = efi_random_alloc(*reserve_size, - MIN_KIMG_ALIGN, reserve_addr, - (u32)phys_seed); - - *image_addr = *reserve_addr + offset; + status = efi_random_alloc(*reserve_size, min_kimg_align, + reserve_addr, phys_seed); } else { status = EFI_OUT_OF_RESOURCES; } @@ -119,7 +98,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, return EFI_SUCCESS; } - *reserve_size = kernel_memsize + TEXT_OFFSET % min_kimg_align; status = efi_low_alloc(*reserve_size, min_kimg_align, reserve_addr); @@ -128,9 +106,9 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, *reserve_size = 0; return status; } - *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align; } + *image_addr = *reserve_addr + TEXT_OFFSET % min_kimg_align; memcpy((void *)*image_addr, _text, kernel_size); return EFI_SUCCESS; -- cgit v1.2.3-70-g09d2 From 43b1df0e013c25abb536699f46d0e9f291b586a0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 27 Mar 2020 16:09:40 +0100 Subject: efi/libstub: Add API function to allocate aligned memory Break out the code to create an aligned page allocation from mem.c and move it into a function efi_allocate_pages_aligned() in alignedmem.c. Update efi_allocate_pages() to invoke it unless the minimum alignment equals the EFI page size (4 KB), in which case the ordinary page allocator is sufficient. This way, efi_allocate_pages_aligned() will only be pulled into the build if it is actually being used (which will be on arm64 only in the immediate future) Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 3 +- drivers/firmware/efi/libstub/alignedmem.c | 57 +++++++++++++++++++++++++++++++ drivers/firmware/efi/libstub/efistub.h | 3 ++ drivers/firmware/efi/libstub/mem.c | 25 +++++--------- 4 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 drivers/firmware/efi/libstub/alignedmem.c (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 75cb2c3a1519..bb8af2b16c49 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -42,7 +42,8 @@ KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ - skip_spaces.o lib-cmdline.o lib-ctype.o + skip_spaces.o lib-cmdline.o lib-ctype.o \ + alignedmem.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c new file mode 100644 index 000000000000..cc89c4d6196f --- /dev/null +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "efistub.h" + +/** + * efi_allocate_pages_aligned() - Allocate memory pages + * @size: minimum number of bytes to allocate + * @addr: On return the address of the first allocated page. The first + * allocated page has alignment EFI_ALLOC_ALIGN which is an + * architecture dependent multiple of the page size. + * @max: the address that the last allocated memory page shall not + * exceed + * @align: minimum alignment of the base of the allocation + * + * Allocate pages as EFI_LOADER_DATA. The allocated pages are aligned according + * to @align, which should be >= EFI_ALLOC_ALIGN. The last allocated page will + * not exceed the address given by @max. + * + * Return: status code + */ +efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, + unsigned long max, unsigned long align) +{ + efi_physical_addr_t alloc_addr; + efi_status_t status; + int slack; + + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + alloc_addr = ALIGN_DOWN(max + 1, align) - 1; + size = round_up(size, EFI_ALLOC_ALIGN); + slack = align / EFI_PAGE_SIZE - 1; + + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, + EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + &alloc_addr); + if (status != EFI_SUCCESS) + return status; + + *addr = ALIGN((unsigned long)alloc_addr, align); + + if (slack > 0) { + int l = (alloc_addr % align) / EFI_PAGE_SIZE; + + if (l) { + efi_bs_call(free_pages, alloc_addr, slack - l + 1); + slack = l - 1; + } + if (slack) + efi_bs_call(free_pages, *addr + size, slack); + } + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 9af65be3b278..baa0bc166074 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -657,6 +657,9 @@ efi_status_t efi_low_alloc(unsigned long size, unsigned long align, efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max); +efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, + unsigned long max, unsigned long align); + efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, unsigned long alloc_size, diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 869a79c8946f..0020b0fa9587 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -93,31 +93,24 @@ fail: efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max) { - efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; - int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1; + efi_physical_addr_t alloc_addr; efi_status_t status; - size = round_up(size, EFI_ALLOC_ALIGN); + if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE) + return efi_allocate_pages_aligned(size, addr, max, + EFI_ALLOC_ALIGN); + + alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, - EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + EFI_LOADER_DATA, DIV_ROUND_UP(size, EFI_PAGE_SIZE), &alloc_addr); if (status != EFI_SUCCESS) return status; - *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN); - - if (slack > 0) { - int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - - if (l) { - efi_bs_call(free_pages, alloc_addr, slack - l + 1); - slack = l - 1; - } - if (slack) - efi_bs_call(free_pages, *addr + size, slack); - } + *addr = alloc_addr; return EFI_SUCCESS; } + /** * efi_low_alloc_above() - allocate pages at or above given address * @size: size of the memory area to allocate -- cgit v1.2.3-70-g09d2 From e71356fe29e7bd5cd5e07800d4940a7481fb0854 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 31 Mar 2020 10:59:39 +0200 Subject: efi/libstub/arm64: Switch to ordinary page allocator for kernel image It is no longer necessary to locate the kernel as low as possible in physical memory, and so we can switch from efi_low_alloc() [which is a rather nasty concoction on top of GetMemoryMap()] to a new helper called efi_allocate_pages_aligned(), which simply rounds up the size to account for the alignment, and frees the misaligned pages again. So considering that the kernel can live anywhere in the physical address space, as long as its alignment requirements are met, let's switch to efi_allocate_pages_aligned() to allocate the pages. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm64-stub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 6fc3bd9a56db..99b67e88a33b 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -98,8 +98,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, return EFI_SUCCESS; } - status = efi_low_alloc(*reserve_size, - min_kimg_align, reserve_addr); + status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, + ULONG_MAX, min_kimg_align); if (status != EFI_SUCCESS) { pr_efi_err("Failed to relocate kernel\n"); -- cgit v1.2.3-70-g09d2 From 685d8164b5da3aeb75b0320fd3a3bf75e4f9c51e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 13 Apr 2020 14:23:57 +0200 Subject: efi/libstub: Move efi_relocate_kernel() into separate source file Move efi_relocate_kernel() into a separate source file, so that it only gets pulled into builds for architectures that use it. Since efi_relocate_kernel() is the only user of efi_low_alloc(), let's move that over as well. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/efistub.h | 15 --- drivers/firmware/efi/libstub/mem.c | 168 ------------------------------ drivers/firmware/efi/libstub/relocate.c | 174 ++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 184 deletions(-) create mode 100644 drivers/firmware/efi/libstub/relocate.c (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index bb8af2b16c49..9a712a6e2f87 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -43,7 +43,7 @@ KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o + alignedmem.o relocate.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index baa0bc166074..2a0698d9dc78 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -639,21 +639,6 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min); - -static inline -efi_status_t efi_low_alloc(unsigned long size, unsigned long align, - unsigned long *addr) -{ - /* - * 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. - */ - return efi_low_alloc_above(size, align, addr, 0x8); -} - efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max); diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 0020b0fa9587..6e0ee6b3d897 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -111,96 +111,6 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, return EFI_SUCCESS; } -/** - * efi_low_alloc_above() - allocate pages at or above given address - * @size: size of the memory area to allocate - * @align: minimum alignment of the allocated memory area. It should - * a power of two. - * @addr: on exit the address of the allocated memory - * @min: minimum address to used for the memory allocation - * - * Allocate at the lowest possible address that is not below @min as - * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at - * least EFI_ALLOC_ALIGN. The first allocated page will not below the address - * given by @min. - * - * Return: status code - */ -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min) -{ - unsigned long map_size, desc_size, buff_size; - efi_memory_desc_t *map; - efi_status_t status; - unsigned long nr_pages; - int i; - struct efi_boot_memmap boot_map; - - boot_map.map = ↦ - boot_map.map_size = &map_size; - boot_map.desc_size = &desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; - - size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = 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_early_memdesc_ptr(m, desc_size, i); - - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (start < min) - start = min; - - start = round_up(start, align); - if ((start + size) > end) - continue; - - status = efi_bs_call(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; - - efi_bs_call(free_pool, map); -fail: - return status; -} - /** * efi_free() - free memory pages * @size: size of the memory area to free in bytes @@ -222,81 +132,3 @@ void efi_free(unsigned long size, unsigned long addr) nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; efi_bs_call(free_pages, addr, nr_pages); } - -/** - * efi_relocate_kernel() - copy memory area - * @image_addr: pointer to address of memory area to copy - * @image_size: size of memory area to copy - * @alloc_size: minimum size of memory to allocate, must be greater or - * equal to image_size - * @preferred_addr: preferred target address - * @alignment: minimum alignment of the allocated memory area. It - * should be a power of two. - * @min_addr: minimum target address - * - * Copy a memory area to a newly allocated memory area aligned according - * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address - * is not available, the allocated address will not be below @min_addr. - * On exit, @image_addr is updated to the target copy address that was used. - * - * This function is used to copy the Linux kernel verbatim. It does not apply - * any relocation changes. - * - * Return: status code - */ -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr) -{ - 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 restrictions on the - * max physical address it can run at. Some architectures - * also have a prefered address, so first try to relocate - * to the preferred address. If that fails, allocate as low - * as possible while respecting the required alignment. - */ - nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &efi_addr); - new_addr = efi_addr; - /* - * If preferred address allocation failed allocate as low as - * possible. - */ - if (status != EFI_SUCCESS) { - status = efi_low_alloc_above(alloc_size, alignment, &new_addr, - min_addr); - } - if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate usable memory for kernel.\n"); - return status; - } - - /* - * 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); - - /* Return the new address of the relocated image. */ - *image_addr = new_addr; - - return status; -} diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c new file mode 100644 index 000000000000..1507d3c82884 --- /dev/null +++ b/drivers/firmware/efi/libstub/relocate.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "efistub.h" + +/** + * efi_low_alloc_above() - allocate pages at or above given address + * @size: size of the memory area to allocate + * @align: minimum alignment of the allocated memory area. It should + * a power of two. + * @addr: on exit the address of the allocated memory + * @min: minimum address to used for the memory allocation + * + * Allocate at the lowest possible address that is not below @min as + * EFI_LOADER_DATA. The allocated pages are aligned according to @align but at + * least EFI_ALLOC_ALIGN. The first allocated page will not below the address + * given by @min. + * + * Return: status code + */ +static efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) +{ + unsigned long map_size, desc_size, buff_size; + efi_memory_desc_t *map; + efi_status_t status; + unsigned long nr_pages; + int i; + struct efi_boot_memmap boot_map; + + boot_map.map = ↦ + boot_map.map_size = &map_size; + boot_map.desc_size = &desc_size; + boot_map.desc_ver = NULL; + boot_map.key_ptr = NULL; + boot_map.buff_size = &buff_size; + + status = efi_get_memory_map(&boot_map); + if (status != EFI_SUCCESS) + goto fail; + + /* + * Enforce minimum alignment that EFI or Linux requires when + * requesting a specific address. We are doing page-based (or + * larger) allocations, and both the address and size must meet + * alignment constraints. + */ + if (align < EFI_ALLOC_ALIGN) + align = EFI_ALLOC_ALIGN; + + size = round_up(size, EFI_ALLOC_ALIGN); + nr_pages = 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_early_memdesc_ptr(m, desc_size, i); + + if (desc->type != EFI_CONVENTIONAL_MEMORY) + continue; + + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + + if (desc->num_pages < nr_pages) + continue; + + start = desc->phys_addr; + end = start + desc->num_pages * EFI_PAGE_SIZE; + + if (start < min) + start = min; + + start = round_up(start, align); + if ((start + size) > end) + continue; + + status = efi_bs_call(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; + + efi_bs_call(free_pool, map); +fail: + return status; +} + +/** + * efi_relocate_kernel() - copy memory area + * @image_addr: pointer to address of memory area to copy + * @image_size: size of memory area to copy + * @alloc_size: minimum size of memory to allocate, must be greater or + * equal to image_size + * @preferred_addr: preferred target address + * @alignment: minimum alignment of the allocated memory area. It + * should be a power of two. + * @min_addr: minimum target address + * + * Copy a memory area to a newly allocated memory area aligned according + * to @alignment but at least EFI_ALLOC_ALIGN. If the preferred address + * is not available, the allocated address will not be below @min_addr. + * On exit, @image_addr is updated to the target copy address that was used. + * + * This function is used to copy the Linux kernel verbatim. It does not apply + * any relocation changes. + * + * Return: status code + */ +efi_status_t efi_relocate_kernel(unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment, + unsigned long min_addr) +{ + 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 restrictions on the + * max physical address it can run at. Some architectures + * also have a prefered address, so first try to relocate + * to the preferred address. If that fails, allocate as low + * as possible while respecting the required alignment. + */ + nr_pages = round_up(alloc_size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &efi_addr); + new_addr = efi_addr; + /* + * If preferred address allocation failed allocate as low as + * possible. + */ + if (status != EFI_SUCCESS) { + status = efi_low_alloc_above(alloc_size, alignment, &new_addr, + min_addr); + } + if (status != EFI_SUCCESS) { + pr_efi_err("Failed to allocate usable memory for kernel.\n"); + return status; + } + + /* + * 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); + + /* Return the new address of the relocated image. */ + *image_addr = new_addr; + + return status; +} -- cgit v1.2.3-70-g09d2 From 420b6d00ca94ce5b6578b1bc12e767ac7a0251ac Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 16 Apr 2020 11:12:25 -0400 Subject: efi/arm: Remove __efistub_global annotation Instead of using __efistub_global to force variables into the .data section, leave them in the .bss but pull the EFI stub's .bss section into .data in the linker script for the compressed kernel. Signed-off-by: Arvind Sankar Reviewed-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20200416151227.3360778-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/vmlinux.lds.S | 2 +- drivers/firmware/efi/libstub/Makefile | 7 ++++--- drivers/firmware/efi/libstub/efistub.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/arch/arm/boot/compressed/vmlinux.lds.S b/arch/arm/boot/compressed/vmlinux.lds.S index b247f399de71..b6793c7932a9 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.S +++ b/arch/arm/boot/compressed/vmlinux.lds.S @@ -78,7 +78,7 @@ SECTIONS * The EFI stub always executes from RAM, and runs strictly before the * decompressor, so we can make an exception for its r/w data, and keep it */ - *(.data.efistub) + *(.data.efistub .bss.efistub) __pecoff_data_end = .; /* diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 9a712a6e2f87..aa3ab9a4105e 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -101,8 +101,9 @@ quiet_cmd_stubcopy = STUBCPY $@ # # ARM discards the .data section because it disallows r/w data in the -# decompressor. So move our .data to .data.efistub, which is preserved -# explicitly by the decompressor linker script. +# decompressor. So move our .data to .data.efistub and .bss to .bss.efistub, +# which are preserved explicitly by the decompressor linker script. # -STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub +STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub \ + --rename-section .bss=.bss.efistub,load,alloc STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 2a0698d9dc78..96e25b17c88e 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -25,7 +25,7 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif -#if defined(CONFIG_ARM) || defined(CONFIG_X86) +#if defined(CONFIG_X86) #define __efistub_global __section(.data) #else #define __efistub_global -- cgit v1.2.3-70-g09d2 From 26a92425f9a301fdeb5482e7891915ce43cc0556 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 16 Apr 2020 11:12:26 -0400 Subject: efi/x86: Remove __efistub_global and add relocation check Instead of using __efistub_global to force variables into the .data section, leave them in the .bss but pull the EFI stub's .bss section into .data in the linker script for the compressed kernel. Add relocation checking for x86 as well to catch non-PC-relative relocations that require runtime processing, since the EFI stub does not do any runtime relocation processing. This will catch, for example, data relocations created by static initializers of pointers. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200416151227.3360778-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/vmlinux.lds.S | 1 + drivers/firmware/efi/libstub/Makefile | 32 +++++++++++++++++++++----------- drivers/firmware/efi/libstub/efistub.h | 4 ---- 3 files changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/arch/x86/boot/compressed/vmlinux.lds.S b/arch/x86/boot/compressed/vmlinux.lds.S index 508cfa6828c5..0dc5c2b9614b 100644 --- a/arch/x86/boot/compressed/vmlinux.lds.S +++ b/arch/x86/boot/compressed/vmlinux.lds.S @@ -52,6 +52,7 @@ SECTIONS _data = . ; *(.data) *(.data.*) + *(.bss.efistub) _edata = . ; } . = ALIGN(L1_CACHE_BYTES); diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index aa3ab9a4105e..8d246b51bd49 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -60,6 +60,25 @@ lib-$(CONFIG_X86) += x86-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +# +# For x86, bootloaders like systemd-boot or grub-efi do not zero-initialize the +# .bss section, so the .bss section of the EFI stub needs to be included in the +# .data section of the compressed kernel to ensure initialization. Rename the +# .bss section here so it's easy to pick out in the linker script. +# +STUBCOPY_FLAGS-$(CONFIG_X86) += --rename-section .bss=.bss.efistub,load,alloc +STUBCOPY_RELOC-$(CONFIG_X86_32) := R_386_32 +STUBCOPY_RELOC-$(CONFIG_X86_64) := R_X86_64_64 + +# +# ARM discards the .data section because it disallows r/w data in the +# decompressor. So move our .data to .data.efistub and .bss to .bss.efistub, +# which are preserved explicitly by the decompressor linker script. +# +STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub \ + --rename-section .bss=.bss.efistub,load,alloc +STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS + # # arm64 puts the stub in the kernel proper, which will unnecessarily retain all # code indefinitely unless it is annotated as __init/__initdata/__initconst etc. @@ -74,8 +93,8 @@ CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) # a verification pass to see if any absolute relocations exist in any of the # object files. # -extra-$(CONFIG_EFI_GENERIC_STUB) := $(lib-y) -lib-$(CONFIG_EFI_GENERIC_STUB) := $(patsubst %.o,%.stub.o,$(lib-y)) +extra-y := $(lib-y) +lib-y := $(patsubst %.o,%.stub.o,$(lib-y)) STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ @@ -98,12 +117,3 @@ quiet_cmd_stubcopy = STUBCPY $@ /bin/false; \ fi; \ $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@ - -# -# ARM discards the .data section because it disallows r/w data in the -# decompressor. So move our .data to .data.efistub and .bss to .bss.efistub, -# which are preserved explicitly by the decompressor linker script. -# -STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub \ - --rename-section .bss=.bss.efistub,load,alloc -STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 96e25b17c88e..5244eacc5d4b 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -25,11 +25,7 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif -#if defined(CONFIG_X86) -#define __efistub_global __section(.data) -#else #define __efistub_global -#endif extern bool __pure nochunk(void); extern bool __pure nokaslr(void); -- cgit v1.2.3-70-g09d2 From 544393707f3ca4f185ea1e41cc3206d1526c99cf Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 16 Apr 2020 11:12:27 -0400 Subject: efi: Kill __efistub_global Now that both arm and x86 are using the linker script to place the EFI stub's global variables in the correct section, remove __efistub_global. Signed-off-by: Arvind Sankar Reviewed-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20200416151227.3360778-4-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 15 +++++++-------- drivers/firmware/efi/libstub/efi-stub.c | 4 ++-- drivers/firmware/efi/libstub/efistub.h | 2 -- drivers/firmware/efi/libstub/gop.c | 2 +- drivers/firmware/efi/libstub/x86-stub.c | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index c6092b6038cf..14e56a64f208 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -12,14 +12,13 @@ #include "efistub.h" -static bool __efistub_global efi_nochunk; -static bool __efistub_global efi_nokaslr; -static bool __efistub_global efi_noinitrd; -static bool __efistub_global efi_quiet; -static bool __efistub_global efi_novamap; -static bool __efistub_global efi_nosoftreserve; -static bool __efistub_global efi_disable_pci_dma = - IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); +static bool efi_nochunk; +static bool efi_nokaslr; +static bool efi_noinitrd; +static bool efi_quiet; +static bool efi_novamap; +static bool efi_nosoftreserve; +static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); bool __pure nochunk(void) { diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 8a26cc11ca4a..8455c590c7b9 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -36,9 +36,9 @@ #endif static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; -static bool __efistub_global flat_va_mapping; +static bool flat_va_mapping; -static efi_system_table_t *__efistub_global sys_table; +static efi_system_table_t *sys_table; __pure efi_system_table_t *efi_system_table(void) { diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 5244eacc5d4b..9a87fff1d4ba 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -25,8 +25,6 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif -#define __efistub_global - extern bool __pure nochunk(void); extern bool __pure nokaslr(void); extern bool __pure noinitrd(void); diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index fa05a0b0adfd..216327d0b034 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -32,7 +32,7 @@ static struct { u8 depth; } res; }; -} cmdline __efistub_global = { .option = EFI_CMDLINE_NONE }; +} cmdline = { .option = EFI_CMDLINE_NONE }; static bool parse_modenum(char *option, char **next) { diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 05ccb229fb45..1c3807d0c321 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -20,7 +20,7 @@ /* Maximum physical address for 64-bit kernel with 4-level paging */ #define MAXMEM_X86_64_4LEVEL (1ull << 46) -static efi_system_table_t *sys_table __efistub_global; +static efi_system_table_t *sys_table; extern const bool efi_is64; extern u32 image_offset; -- cgit v1.2.3-70-g09d2 From ccc27ae77494252965aedca68588e7457829cfad Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 16 Apr 2020 18:38:06 +0200 Subject: efi/libstub: Drop __pure getter for efi_system_table The practice of using __pure getter functions to access global variables in the EFI stub dates back to the time when we had to carefully prevent GOT entries from being emitted, because we could not rely on the toolchain to do this for us. Today, we use the hidden visibility pragma for all EFI stub source files, which now all live in the same subdirectory, and we apply a sanity check on the objects, so we can get rid of these getter functions and simply refer to global data objects directly. Start with efi_system_table(), and convert it into a global variable. While at it, make it a pointer-to-const, because we can. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 14 ++++++++------ drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +++--- drivers/firmware/efi/libstub/efi-stub.c | 13 ++++--------- drivers/firmware/efi/libstub/efistub.h | 6 +++--- drivers/firmware/efi/libstub/fdt.c | 6 +++--- drivers/firmware/efi/libstub/x86-stub.c | 23 +++++++++-------------- 6 files changed, 30 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index f59cba117dcb..78e839925a81 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -339,15 +339,17 @@ static inline u32 efi64_convert_status(efi_status_t status) #define efi_bs_call(func, ...) \ (efi_is_native() \ - ? efi_system_table()->boottime->func(__VA_ARGS__) \ - : __efi64_thunk_map(efi_table_attr(efi_system_table(), \ - boottime), func, __VA_ARGS__)) + ? efi_system_table->boottime->func(__VA_ARGS__) \ + : __efi64_thunk_map(efi_table_attr(efi_system_table, \ + boottime), \ + func, __VA_ARGS__)) #define efi_rt_call(func, ...) \ (efi_is_native() \ - ? efi_system_table()->runtime->func(__VA_ARGS__) \ - : __efi64_thunk_map(efi_table_attr(efi_system_table(), \ - runtime), func, __VA_ARGS__)) + ? efi_system_table->runtime->func(__VA_ARGS__) \ + : __efi64_thunk_map(efi_table_attr(efi_system_table, \ + runtime), \ + func, __VA_ARGS__)) extern bool efi_reboot_required(void); extern bool efi_is_table_address(unsigned long phys_addr); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 14e56a64f208..0b1688b10ddc 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -287,8 +287,8 @@ fail: void *get_efi_config_table(efi_guid_t guid) { - unsigned long tables = efi_table_attr(efi_system_table(), tables); - int nr_tables = efi_table_attr(efi_system_table(), nr_tables); + unsigned long tables = efi_table_attr(efi_system_table, tables); + int nr_tables = efi_table_attr(efi_system_table, nr_tables); int i; for (i = 0; i < nr_tables; i++) { @@ -305,7 +305,7 @@ void *get_efi_config_table(efi_guid_t guid) void efi_char16_printk(efi_char16_t *str) { - efi_call_proto(efi_table_attr(efi_system_table(), con_out), + efi_call_proto(efi_table_attr(efi_system_table, con_out), output_string, str); } diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 8455c590c7b9..8edfd4022803 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -38,12 +38,7 @@ static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; static bool flat_va_mapping; -static efi_system_table_t *sys_table; - -__pure efi_system_table_t *efi_system_table(void) -{ - return sys_table; -} +const efi_system_table_t *efi_system_table; static struct screen_info *setup_graphics(void) { @@ -167,10 +162,10 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) efi_properties_table_t *prop_tbl; unsigned long max_addr; - sys_table = sys_table_arg; + efi_system_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { + if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { status = EFI_INVALID_PARAMETER; goto fail; } @@ -184,7 +179,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) * information about the running image, such as size and the command * line. */ - status = sys_table->boottime->handle_protocol(handle, + status = efi_system_table->boottime->handle_protocol(handle, &loaded_image_proto, (void *)&image); if (status != EFI_SUCCESS) { pr_efi_err("Failed to get loaded image protocol\n"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 9a87fff1d4ba..c1481c5abea4 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -31,13 +31,13 @@ extern bool __pure noinitrd(void); extern bool __pure is_quiet(void); extern bool __pure novamap(void); -extern __pure efi_system_table_t *efi_system_table(void); +extern const efi_system_table_t *efi_system_table; #ifndef efi_bs_call -#define efi_bs_call(func, ...) efi_system_table()->boottime->func(__VA_ARGS__) +#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__) #endif #ifndef efi_rt_call -#define efi_rt_call(func, ...) efi_system_table()->runtime->func(__VA_ARGS__) +#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__) #endif #ifndef efi_is_native #define efi_is_native() (true) diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 46cffac7a5f1..06d5e7fc8e34 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -110,7 +110,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, /* Add FDT entries for EFI runtime services in chosen node. */ node = fdt_subnode_offset(fdt, 0, "chosen"); - fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table()); + fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table); status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64); if (status) @@ -314,7 +314,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, return EFI_SUCCESS; /* Install the new virtual address map */ - svam = efi_system_table()->runtime->set_virtual_address_map; + svam = efi_system_table->runtime->set_virtual_address_map; status = svam(runtime_entry_count * desc_size, desc_size, desc_ver, runtime_map); @@ -348,7 +348,7 @@ fail_free_new_fdt: efi_free(MAX_FDT_SIZE, *new_fdt_addr); fail: - efi_system_table()->boottime->free_pool(runtime_map); + efi_system_table->boottime->free_pool(runtime_map); return EFI_LOAD_ERROR; } diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 1c3807d0c321..3f132f51ab0f 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -20,15 +20,10 @@ /* Maximum physical address for 64-bit kernel with 4-level paging */ #define MAXMEM_X86_64_4LEVEL (1ull << 46) -static efi_system_table_t *sys_table; +const efi_system_table_t *efi_system_table; extern const bool efi_is64; extern u32 image_offset; -__pure efi_system_table_t *efi_system_table(void) -{ - return sys_table; -} - __attribute_const__ bool efi_is_64bit(void) { if (IS_ENABLED(CONFIG_EFI_MIXED)) @@ -227,7 +222,7 @@ static const efi_char16_t apple[] = L"Apple"; static void setup_quirks(struct boot_params *boot_params) { efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long) - efi_table_attr(efi_system_table(), fw_vendor); + efi_table_attr(efi_system_table, fw_vendor); if (!memcmp(fw_vendor, apple, sizeof(apple))) { if (IS_ENABLED(CONFIG_APPLE_PROPERTIES)) @@ -377,10 +372,10 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, unsigned long ramdisk_addr; unsigned long ramdisk_size; - sys_table = sys_table_arg; + efi_system_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) efi_exit(handle, EFI_INVALID_PARAMETER); status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image); @@ -446,7 +441,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, } } - efi_stub_entry(handle, sys_table, boot_params); + efi_stub_entry(handle, sys_table_arg, boot_params); /* not reached */ fail2: @@ -651,14 +646,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, : EFI32_LOADER_SIGNATURE; memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32)); - p->efi->efi_systab = (unsigned long)efi_system_table(); + p->efi->efi_systab = (unsigned long)efi_system_table; p->efi->efi_memdesc_size = *map->desc_size; p->efi->efi_memdesc_version = *map->desc_ver; p->efi->efi_memmap = (unsigned long)*map->map; p->efi->efi_memmap_size = *map->map_size; #ifdef CONFIG_X86_64 - p->efi->efi_systab_hi = (unsigned long)efi_system_table() >> 32; + p->efi->efi_systab_hi = (unsigned long)efi_system_table >> 32; p->efi->efi_memmap_hi = (unsigned long)*map->map >> 32; #endif @@ -719,10 +714,10 @@ unsigned long efi_main(efi_handle_t handle, efi_status_t status; unsigned long cmdline_paddr; - sys_table = sys_table_arg; + efi_system_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) efi_exit(handle, EFI_INVALID_PARAMETER); /* -- cgit v1.2.3-70-g09d2 From 980771f616624e4c563a2014a32b7d23876d1ecf Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 16 Apr 2020 18:45:24 +0200 Subject: efi/libstub: Drop __pure getters for EFI stub options The practice of using __pure getter functions to access global variables in the EFI stub dates back to the time when we had to carefully prevent GOT entries from being emitted, because we could not rely on the toolchain to do this for us. Today, we use the hidden visibility pragma for all EFI stub source files, which now all live in the same subdirectory, and we apply a sanity check on the objects, so we can get rid of these getter functions and simply refer to global data objects directly. So switch over the remaining boolean variables carrying options set on the kernel command line. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm64-stub.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 31 +++++--------------------- drivers/firmware/efi/libstub/efi-stub.c | 6 ++--- drivers/firmware/efi/libstub/efistub.h | 12 +++++----- drivers/firmware/efi/libstub/fdt.c | 2 +- drivers/firmware/efi/libstub/file.c | 2 +- drivers/firmware/efi/libstub/x86-stub.c | 4 ++-- 7 files changed, 20 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 99b67e88a33b..ba4db35015a3 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -55,7 +55,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, u32 phys_seed = 0; if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { - if (!nokaslr()) { + if (!efi_nokaslr) { status = efi_get_random_bytes(sizeof(phys_seed), (u8 *)&phys_seed); if (status == EFI_NOT_FOUND) { diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0b1688b10ddc..1c92ac231f94 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -12,34 +12,15 @@ #include "efistub.h" -static bool efi_nochunk; -static bool efi_nokaslr; -static bool efi_noinitrd; -static bool efi_quiet; -static bool efi_novamap; +bool efi_nochunk; +bool efi_nokaslr; +bool efi_noinitrd; +bool efi_quiet; +bool efi_novamap; + static bool efi_nosoftreserve; static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); -bool __pure nochunk(void) -{ - return efi_nochunk; -} -bool __pure nokaslr(void) -{ - return efi_nokaslr; -} -bool __pure noinitrd(void) -{ - return efi_noinitrd; -} -bool __pure is_quiet(void) -{ - return efi_quiet; -} -bool __pure novamap(void) -{ - return efi_novamap; -} bool __pure __efi_soft_reserve_enabled(void) { return !efi_nosoftreserve; diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 8edfd4022803..ee225b323687 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -263,7 +263,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - if (!noinitrd()) { + if (!efi_noinitrd) { max_addr = efi_get_max_initrd_addr(dram_base, image_addr); status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); @@ -294,7 +294,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); /* hibernation expects the runtime regions to stay in the same place */ - if (!IS_ENABLED(CONFIG_HIBERNATION) && !nokaslr() && !flat_va_mapping) { + if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) { /* * Randomize the base of the UEFI runtime services region. * Preserve the 2 MB alignment of the region by taking a @@ -367,7 +367,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, size = in->num_pages * EFI_PAGE_SIZE; in->virt_addr = in->phys_addr; - if (novamap()) { + if (efi_novamap) { continue; } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index c1481c5abea4..5ff63230a1f1 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -25,11 +25,11 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif -extern bool __pure nochunk(void); -extern bool __pure nokaslr(void); -extern bool __pure noinitrd(void); -extern bool __pure is_quiet(void); -extern bool __pure novamap(void); +extern bool efi_nochunk; +extern bool efi_nokaslr; +extern bool efi_noinitrd; +extern bool efi_quiet; +extern bool efi_novamap; extern const efi_system_table_t *efi_system_table; @@ -50,7 +50,7 @@ extern const efi_system_table_t *efi_system_table; #endif #define pr_efi(msg) do { \ - if (!is_quiet()) efi_printk("EFI stub: "msg); \ + if (!efi_quiet) efi_printk("EFI stub: "msg); \ } while (0) #define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg) diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 06d5e7fc8e34..3074a5e27c65 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -310,7 +310,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, if (status == EFI_SUCCESS) { efi_set_virtual_address_map_t *svam; - if (novamap()) + if (efi_novamap) return EFI_SUCCESS; /* Install the new virtual address map */ diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 27e014ea4459..50aaf15f9ad5 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -142,7 +142,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, if (!load_addr || !load_size) return EFI_INVALID_PARAMETER; - if (IS_ENABLED(CONFIG_X86) && !nochunk()) + if (IS_ENABLED(CONFIG_X86) && !efi_nochunk) efi_chunk_size = EFI_READ_CHUNK_SIZE; alloc_addr = alloc_size = 0; diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 3f132f51ab0f..bddbc103a34b 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -427,7 +427,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, if (status != EFI_SUCCESS) goto fail2; - if (!noinitrd()) { + if (!efi_noinitrd) { status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size, hdr->initrd_addr_max, @@ -787,7 +787,7 @@ unsigned long efi_main(efi_handle_t handle, * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device * path to supersede it. */ - if (!noinitrd()) { + if (!efi_noinitrd) { unsigned long addr, size; status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX); -- cgit v1.2.3-70-g09d2 From 0a75561489f534cf2e8f6883e0cf8cdf51c534c5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 16 Apr 2020 21:15:23 +0200 Subject: efi/libstub/x86: Avoid getter function for efi_is64 We no longer need to take special care when using global variables in the EFI stub, so switch to a simple symbol reference for efi_is64. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 11 ++++++++--- drivers/firmware/efi/libstub/x86-stub.c | 8 -------- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 78e839925a81..cd0c3fbf6156 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -225,14 +225,19 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, /* arch specific definitions used by the stub code */ -__attribute_const__ bool efi_is_64bit(void); +extern const bool efi_is64; + +static inline bool efi_is_64bit(void) +{ + if (IS_ENABLED(CONFIG_EFI_MIXED)) + return efi_is64; + return IS_ENABLED(CONFIG_X86_64); +} static inline bool efi_is_native(void) { if (!IS_ENABLED(CONFIG_X86_64)) return true; - if (!IS_ENABLED(CONFIG_EFI_MIXED)) - return true; return efi_is_64bit(); } diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index bddbc103a34b..597793fe8d22 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -21,16 +21,8 @@ #define MAXMEM_X86_64_4LEVEL (1ull << 46) const efi_system_table_t *efi_system_table; -extern const bool efi_is64; extern u32 image_offset; -__attribute_const__ bool efi_is_64bit(void) -{ - if (IS_ENABLED(CONFIG_EFI_MIXED)) - return efi_is64; - return IS_ENABLED(CONFIG_X86_64); -} - static efi_status_t preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) { -- cgit v1.2.3-70-g09d2 From 4e9a0f73f030e19a9259b69a7079021048e1f904 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Mar 2020 09:24:14 +0100 Subject: efi: Clean up config table description arrays Increase legibility by adding whitespace to the efi_config_table_type_t arrays that describe which EFI config tables we look for when going over the firmware provided list. While at it, replace the 'name' char pointer with a char array, which is more space efficient on relocatable 64-bit kernels, as it avoids a 8 byte pointer and the associated relocation data (24 bytes when using RELA format) Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 12 ++++++------ arch/x86/platform/efi/efi.c | 8 ++++---- drivers/firmware/efi/arm-init.c | 4 ++-- drivers/firmware/efi/efi.c | 28 ++++++++++++++-------------- include/linux/efi.h | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index f69f3fe0532e..a54eacbc61a9 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -57,12 +57,12 @@ unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; static const efi_config_table_type_t arch_tables[] __initconst = { - {ESI_TABLE_GUID, "ESI", &esi_phys}, - {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, - {MPS_TABLE_GUID, "MPS", &mps_phys}, - {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, - {SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys}, - {NULL_GUID, NULL, 0}, + {ESI_TABLE_GUID, &esi_phys, "ESI" }, + {HCDP_TABLE_GUID, &hcdp_phys, "HCDP" }, + {MPS_TABLE_GUID, &mps_phys, "MPS" }, + {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, &palo_phys, "PALO" }, + {SAL_SYSTEM_TABLE_GUID, &sal_systab_phys, "SALsystab" }, + {}, }; extern efi_status_t efi_call_phys (void *, ...); diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 1aae5302501d..e966115d105c 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -62,12 +62,12 @@ static unsigned long efi_runtime, efi_nr_tables; unsigned long efi_fw_vendor, efi_config_table; static const efi_config_table_type_t arch_tables[] __initconst = { - {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, - {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys}, + {EFI_PROPERTIES_TABLE_GUID, &prop_phys, "PROP" }, + {UGA_IO_PROTOCOL_GUID, &uga_phys, "UGA" }, #ifdef CONFIG_X86_UV - {UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys}, + {UV_SYSTEM_TABLE_GUID, &uv_systab_phys, "UVsystab" }, #endif - {NULL_GUID, NULL, NULL}, + {}, }; static const unsigned long * const efi_tables[] = { diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 9e5e62f5f94d..c697e70ca7e7 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -54,8 +54,8 @@ static phys_addr_t __init efi_to_phys(unsigned long addr) static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR; static const efi_config_table_type_t arch_tables[] __initconst = { - {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table}, - {NULL_GUID, NULL, NULL} + {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table}, + {} }; static void __init init_screen_info(void) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 911a2bd0f6b7..e49c0b6db988 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -502,21 +502,21 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) } static const efi_config_table_type_t common_tables[] __initconst = { - {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, - {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, - {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, - {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, - {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, - {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table}, - {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi_rng_seed}, - {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, - {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, - {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, - {EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop}, + {ACPI_20_TABLE_GUID, &efi.acpi20, "ACPI 2.0" }, + {ACPI_TABLE_GUID, &efi.acpi, "ACPI" }, + {SMBIOS_TABLE_GUID, &efi.smbios, "SMBIOS" }, + {SMBIOS3_TABLE_GUID, &efi.smbios3, "SMBIOS 3.0" }, + {EFI_SYSTEM_RESOURCE_TABLE_GUID, &efi.esrt, "ESRT" }, + {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, &efi_mem_attr_table, "MEMATTR" }, + {LINUX_EFI_RANDOM_SEED_TABLE_GUID, &efi_rng_seed, "RNG" }, + {LINUX_EFI_TPM_EVENT_LOG_GUID, &efi.tpm_log, "TPMEventLog" }, + {LINUX_EFI_TPM_FINAL_LOG_GUID, &efi.tpm_final_log, "TPMFinalLog" }, + {LINUX_EFI_MEMRESERVE_TABLE_GUID, &mem_reserve, "MEMRESERVE" }, + {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, #ifdef CONFIG_EFI_RCI2_TABLE - {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, + {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, #endif - {NULL_GUID, NULL, NULL}, + {}, }; static __init int match_config_table(const efi_guid_t *guid, @@ -529,7 +529,7 @@ static __init int match_config_table(const efi_guid_t *guid, for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { if (!efi_guidcmp(*guid, table_types[i].guid)) { *(table_types[i].ptr) = table; - if (table_types[i].name) + if (table_types[i].name[0]) pr_cont(" %s=0x%lx ", table_types[i].name, table); return 1; diff --git a/include/linux/efi.h b/include/linux/efi.h index 251f1f783cdf..9b7c7ec319ac 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -379,8 +379,8 @@ typedef union { typedef struct { efi_guid_t guid; - const char *name; unsigned long *ptr; + const char name[16]; } efi_config_table_type_t; #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) -- cgit v1.2.3-70-g09d2 From 4eb8320bd1aaa7e69d039f2c251735e3ef0b9a38 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 26 Mar 2020 09:34:35 +0100 Subject: efi: Move arch_tables check to caller Instead of making match_config_table() test its table_types pointer for NULL-ness, omit the call entirely if no arch_tables pointer was provided to efi_config_parse_tables(). Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index e49c0b6db988..48d0188936c3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -525,15 +525,13 @@ static __init int match_config_table(const efi_guid_t *guid, { int i; - if (table_types) { - for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { - if (!efi_guidcmp(*guid, table_types[i].guid)) { - *(table_types[i].ptr) = table; - if (table_types[i].name[0]) - pr_cont(" %s=0x%lx ", - table_types[i].name, table); - return 1; - } + for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { + if (!efi_guidcmp(*guid, table_types[i].guid)) { + *(table_types[i].ptr) = table; + if (table_types[i].name[0]) + pr_cont("%s=0x%lx ", + table_types[i].name, table); + return 1; } } @@ -570,7 +568,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, table = tbl32[i].table; } - if (!match_config_table(guid, table, common_tables)) + if (!match_config_table(guid, table, common_tables) && arch_tables) match_config_table(guid, table, arch_tables); } pr_cont("\n"); -- cgit v1.2.3-70-g09d2 From 4da0b2b7e67524cc206067865666899bc02e1cb0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 25 Apr 2020 12:22:04 +0200 Subject: efi/libstub: Re-enable command line initrd loading for x86 Commit: cf6b83664895a5 ("efi/libstub: Make initrd file loader configurable") inadvertently disabled support on x86 for loading an initrd passed via the initrd= option on the kernel command line. Add X86 to the newly introduced Kconfig option's title and depends declarations, so it gets enabled by default, as before. Signed-off-by: Ard Biesheuvel Signed-off-by: Ingo Molnar Cc: linux-efi@vger.kernel.org --- drivers/firmware/efi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 4e788dd55b03..6b38f9e5d203 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -125,8 +125,8 @@ config EFI_ARMSTUB_DTB_LOADER this option is necessary. config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER - bool "Enable the command line initrd loader" - depends on EFI_GENERIC_STUB + bool "Enable the command line initrd loader" if !X86 + depends on EFI_STUB && (EFI_GENERIC_STUB || X86) default y help Select this config option to add support for the initrd= command -- cgit v1.2.3-70-g09d2 From 019512f1fd373632577ecd3e8d721e48b71fb36a Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:33 -0400 Subject: efi/x86: Use correct size for boot_params struct boot_params is only 4096 bytes, not 16384. Fix this by using sizeof(struct boot_params) instead of hardcoding the incorrect value. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/x86-stub.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 597793fe8d22..d4bafd7f6f9f 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -379,13 +379,14 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, image_base = efi_table_attr(image, image_base); image_offset = (void *)startup_32 - image_base; - status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX); + status = efi_allocate_pages(sizeof(struct boot_params), + (unsigned long *)&boot_params, ULONG_MAX); if (status != EFI_SUCCESS) { efi_printk("Failed to allocate lowmem for boot params\n"); efi_exit(handle, status); } - memset(boot_params, 0x0, 0x4000); + memset(boot_params, 0x0, sizeof(struct boot_params)); hdr = &boot_params->hdr; @@ -439,7 +440,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, fail2: efi_free(options_size, (unsigned long)cmdline_ptr); fail: - efi_free(0x4000, (unsigned long)boot_params); + efi_free(sizeof(struct boot_params), (unsigned long)boot_params); efi_exit(handle, status); } -- cgit v1.2.3-70-g09d2 From eed4e0193ee7b00213791f7732bce0fc0c54f3b3 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:34 -0400 Subject: efi/libstub: Add a helper function to split 64-bit values In several places 64-bit values need to be split up into two 32-bit fields, in order to be backward-compatible with the old 32-bit ABIs. Instead of open-coding this, add a helper function to set a 64-bit value as two 32-bit fields. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 7 +++++++ drivers/firmware/efi/libstub/gop.c | 6 ++---- drivers/firmware/efi/libstub/x86-stub.c | 32 ++++++++++++++------------------ 3 files changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 5ff63230a1f1..e8aa70ba08d5 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -87,6 +87,13 @@ extern const efi_system_table_t *efi_system_table; ((handle = efi_get_handle_at((array), i)) || true); \ i++) +static inline +void efi_set_u64_split(u64 data, u32 *lo, u32 *hi) +{ + *lo = lower_32_bits(data); + *hi = upper_32_bits(data); +} + /* * Allocation types for calls to boottime->allocate_pages. */ diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 216327d0b034..64cee0febae0 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -422,7 +422,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, efi_graphics_output_protocol_t *gop; efi_graphics_output_protocol_mode_t *mode; efi_graphics_output_mode_info_t *info; - efi_physical_addr_t fb_base; gop = find_gop(proto, size, handles); @@ -442,9 +441,8 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, si->lfb_width = info->horizontal_resolution; si->lfb_height = info->vertical_resolution; - fb_base = efi_table_attr(mode, frame_buffer_base); - si->lfb_base = lower_32_bits(fb_base); - si->ext_lfb_base = upper_32_bits(fb_base); + efi_set_u64_split(efi_table_attr(mode, frame_buffer_base), + &si->lfb_base, &si->ext_lfb_base); if (si->ext_lfb_base) si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index d4bafd7f6f9f..f91d4aab0156 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -408,9 +408,8 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, if (!cmdline_ptr) goto fail; - hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; - /* Fill in upper bits of command line address, NOP on 32 bit */ - boot_params->ext_cmd_line_ptr = (u64)(unsigned long)cmdline_ptr >> 32; + efi_set_u64_split((unsigned long)cmdline_ptr, + &hdr->cmd_line_ptr, &boot_params->ext_cmd_line_ptr); hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; @@ -427,10 +426,10 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, ULONG_MAX); if (status != EFI_SUCCESS) goto fail2; - hdr->ramdisk_image = ramdisk_addr & 0xffffffff; - hdr->ramdisk_size = ramdisk_size & 0xffffffff; - boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32; - boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32; + efi_set_u64_split(ramdisk_addr, &hdr->ramdisk_image, + &boot_params->ext_ramdisk_image); + efi_set_u64_split(ramdisk_size, &hdr->ramdisk_size, + &boot_params->ext_ramdisk_size); } } @@ -639,17 +638,14 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, : EFI32_LOADER_SIGNATURE; memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32)); - p->efi->efi_systab = (unsigned long)efi_system_table; + efi_set_u64_split((unsigned long)efi_system_table, + &p->efi->efi_systab, &p->efi->efi_systab_hi); p->efi->efi_memdesc_size = *map->desc_size; p->efi->efi_memdesc_version = *map->desc_ver; - p->efi->efi_memmap = (unsigned long)*map->map; + efi_set_u64_split((unsigned long)*map->map, + &p->efi->efi_memmap, &p->efi->efi_memmap_hi); p->efi->efi_memmap_size = *map->map_size; -#ifdef CONFIG_X86_64 - p->efi->efi_systab_hi = (unsigned long)efi_system_table >> 32; - p->efi->efi_memmap_hi = (unsigned long)*map->map >> 32; -#endif - return EFI_SUCCESS; } @@ -785,10 +781,10 @@ unsigned long efi_main(efi_handle_t handle, status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX); if (status == EFI_SUCCESS) { - hdr->ramdisk_image = (u32)addr; - hdr->ramdisk_size = (u32)size; - boot_params->ext_ramdisk_image = (u64)addr >> 32; - boot_params->ext_ramdisk_size = (u64)size >> 32; + efi_set_u64_split(addr, &hdr->ramdisk_image, + &boot_params->ext_ramdisk_image); + efi_set_u64_split(size, &hdr->ramdisk_size, + &boot_params->ext_ramdisk_size); } else if (status != EFI_NOT_FOUND) { efi_printk("efi_load_initrd_dev_path() failed!\n"); goto fail; -- cgit v1.2.3-70-g09d2 From 793473c28a4b17ade3c2bb69782fb0b55acb6e0b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:35 -0400 Subject: efi/libstub: Move pr_efi/pr_efi_err into efi namespace Rename pr_efi to efi_info and pr_efi_err to efi_err to make it more obvious that they are part of the EFI stub and not generic printk infra. Suggested-by: Joe Perches Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-4-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm32-stub.c | 12 ++++++------ drivers/firmware/efi/libstub/arm64-stub.c | 14 +++++++------- drivers/firmware/efi/libstub/efi-stub.c | 32 +++++++++++++++---------------- drivers/firmware/efi/libstub/efistub.h | 4 ++-- drivers/firmware/efi/libstub/fdt.c | 16 ++++++++-------- drivers/firmware/efi/libstub/file.c | 12 ++++++------ drivers/firmware/efi/libstub/pci.c | 8 ++++---- drivers/firmware/efi/libstub/relocate.c | 2 +- drivers/firmware/efi/libstub/secureboot.c | 4 ++-- 9 files changed, 52 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index 7826553af2ba..b038afe2ee7a 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -18,7 +18,7 @@ efi_status_t check_platform_features(void) /* LPAE kernels need compatible hardware */ block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0); if (block < 5) { - pr_efi_err("This LPAE kernel is not supported by your CPU\n"); + efi_err("This LPAE kernel is not supported by your CPU\n"); return EFI_UNSUPPORTED; } return EFI_SUCCESS; @@ -120,7 +120,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base, */ status = efi_get_memory_map(&map); if (status != EFI_SUCCESS) { - pr_efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n"); + efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n"); return status; } @@ -162,7 +162,7 @@ static efi_status_t reserve_kernel_base(unsigned long dram_base, (end - start) / EFI_PAGE_SIZE, &start); if (status != EFI_SUCCESS) { - pr_efi_err("reserve_kernel_base(): alloc failed.\n"); + efi_err("reserve_kernel_base(): alloc failed.\n"); goto out; } break; @@ -219,7 +219,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size); if (status != EFI_SUCCESS) { - pr_efi_err("Unable to allocate memory for uncompressed kernel.\n"); + efi_err("Unable to allocate memory for uncompressed kernel.\n"); return status; } @@ -232,7 +232,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, status = efi_relocate_kernel(image_addr, *image_size, *image_size, kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to relocate kernel.\n"); + efi_err("Failed to relocate kernel.\n"); efi_free(*reserve_size, *reserve_addr); *reserve_size = 0; return status; @@ -244,7 +244,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * address at which the zImage is loaded. */ if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) { - pr_efi_err("Failed to relocate kernel, no low memory available.\n"); + efi_err("Failed to relocate kernel, no low memory available.\n"); efi_free(*reserve_size, *reserve_addr); *reserve_size = 0; efi_free(*image_size, *image_addr); diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index ba4db35015a3..7f6a57dec513 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -26,9 +26,9 @@ efi_status_t check_platform_features(void) tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf; if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) { if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) - pr_efi_err("This 64 KB granular kernel is not supported by your CPU\n"); + efi_err("This 64 KB granular kernel is not supported by your CPU\n"); else - pr_efi_err("This 16 KB granular kernel is not supported by your CPU\n"); + efi_err("This 16 KB granular kernel is not supported by your CPU\n"); return EFI_UNSUPPORTED; } return EFI_SUCCESS; @@ -59,18 +59,18 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, status = efi_get_random_bytes(sizeof(phys_seed), (u8 *)&phys_seed); if (status == EFI_NOT_FOUND) { - pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); + efi_info("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); } else if (status != EFI_SUCCESS) { - pr_efi_err("efi_get_random_bytes() failed\n"); + efi_err("efi_get_random_bytes() failed\n"); return status; } } else { - pr_efi("KASLR disabled on kernel command line\n"); + efi_info("KASLR disabled on kernel command line\n"); } } if (image->image_base != _text) - pr_efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); + efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); kernel_size = _edata - _text; kernel_memsize = kernel_size + (_end - _edata); @@ -102,7 +102,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, ULONG_MAX, min_kimg_align); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to relocate kernel\n"); + efi_err("Failed to relocate kernel\n"); *reserve_size = 0; return status; } diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index ee225b323687..72ffd2670f99 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -69,7 +69,7 @@ static void install_memreserve_table(void) status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), (void **)&rsv); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate memreserve entry!\n"); + efi_err("Failed to allocate memreserve entry!\n"); return; } @@ -80,7 +80,7 @@ static void install_memreserve_table(void) status = efi_bs_call(install_configuration_table, &memreserve_table_guid, rsv); if (status != EFI_SUCCESS) - pr_efi_err("Failed to install memreserve config table!\n"); + efi_err("Failed to install memreserve config table!\n"); } static unsigned long get_dram_base(void) @@ -182,13 +182,13 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) status = efi_system_table->boottime->handle_protocol(handle, &loaded_image_proto, (void *)&image); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to get loaded image protocol\n"); + efi_err("Failed to get loaded image protocol\n"); goto fail; } dram_base = get_dram_base(); if (dram_base == EFI_ERROR) { - pr_efi_err("Failed to find DRAM base\n"); + efi_err("Failed to find DRAM base\n"); status = EFI_LOAD_ERROR; goto fail; } @@ -200,7 +200,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) */ cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); if (!cmdline_ptr) { - pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); + efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); status = EFI_OUT_OF_RESOURCES; goto fail; } @@ -213,7 +213,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) efi_parse_options(cmdline_ptr); - pr_efi("Booting Linux Kernel...\n"); + efi_info("Booting Linux Kernel...\n"); si = setup_graphics(); @@ -222,7 +222,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) &reserve_size, dram_base, image); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to relocate kernel\n"); + efi_err("Failed to relocate kernel\n"); goto fail_free_cmdline; } @@ -241,42 +241,42 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) || secure_boot != efi_secureboot_mode_disabled) { if (strstr(cmdline_ptr, "dtb=")) - pr_efi("Ignoring DTB from command line.\n"); + efi_info("Ignoring DTB from command line.\n"); } else { status = efi_load_dtb(image, &fdt_addr, &fdt_size); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to load device tree!\n"); + efi_err("Failed to load device tree!\n"); goto fail_free_image; } } if (fdt_addr) { - pr_efi("Using DTB from command line\n"); + efi_info("Using DTB from command line\n"); } else { /* Look for a device tree configuration table entry. */ fdt_addr = (uintptr_t)get_fdt(&fdt_size); if (fdt_addr) - pr_efi("Using DTB from configuration table\n"); + efi_info("Using DTB from configuration table\n"); } if (!fdt_addr) - pr_efi("Generating empty DTB\n"); + efi_info("Generating empty DTB\n"); if (!efi_noinitrd) { max_addr = efi_get_max_initrd_addr(dram_base, image_addr); status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); if (status == EFI_SUCCESS) { - pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); } else if (status == EFI_NOT_FOUND) { status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, max_addr); if (status == EFI_SUCCESS && initrd_size > 0) - pr_efi("Loaded initrd from command line option\n"); + efi_info("Loaded initrd from command line option\n"); } if (status != EFI_SUCCESS) - pr_efi_err("Failed to load initrd!\n"); + efi_err("Failed to load initrd!\n"); } efi_random_get_seed(); @@ -326,7 +326,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) /* not reached */ fail_free_initrd: - pr_efi_err("Failed to update FDT and exit boot services\n"); + efi_err("Failed to update FDT and exit boot services\n"); efi_free(initrd_size, initrd_addr); efi_free(fdt_size, fdt_addr); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index e8aa70ba08d5..8c905a1be1b4 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -49,11 +49,11 @@ extern const efi_system_table_t *efi_system_table; #define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) #endif -#define pr_efi(msg) do { \ +#define efi_info(msg) do { \ if (!efi_quiet) efi_printk("EFI stub: "msg); \ } while (0) -#define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg) +#define efi_err(msg) efi_printk("EFI stub: ERROR: "msg) /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 3074a5e27c65..11ecf3c4640e 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -39,7 +39,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, /* Do some checks on provided FDT, if it exists: */ if (orig_fdt) { if (fdt_check_header(orig_fdt)) { - pr_efi_err("Device Tree header not valid!\n"); + efi_err("Device Tree header not valid!\n"); return EFI_LOAD_ERROR; } /* @@ -47,7 +47,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, * configuration table: */ if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { - pr_efi_err("Truncated device tree! foo!\n"); + efi_err("Truncated device tree! foo!\n"); return EFI_LOAD_ERROR; } } @@ -270,16 +270,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, */ status = efi_get_memory_map(&map); if (status != EFI_SUCCESS) { - pr_efi_err("Unable to retrieve UEFI memory map.\n"); + efi_err("Unable to retrieve UEFI memory map.\n"); return status; } - pr_efi("Exiting boot services and installing virtual address map...\n"); + efi_info("Exiting boot services and installing virtual address map...\n"); map.map = &memory_map; status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { - pr_efi_err("Unable to allocate memory for new device tree.\n"); + efi_err("Unable to allocate memory for new device tree.\n"); goto fail; } @@ -296,7 +296,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, initrd_addr, initrd_size); if (status != EFI_SUCCESS) { - pr_efi_err("Unable to construct new device tree.\n"); + efi_err("Unable to construct new device tree.\n"); goto fail_free_new_fdt; } @@ -342,7 +342,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, return EFI_SUCCESS; } - pr_efi_err("Exit boot services failed.\n"); + efi_err("Exit boot services failed.\n"); fail_free_new_fdt: efi_free(MAX_FDT_SIZE, *new_fdt_addr); @@ -363,7 +363,7 @@ void *get_fdt(unsigned long *fdt_size) return NULL; if (fdt_check_header(fdt) != 0) { - pr_efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n"); + efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n"); return NULL; } *fdt_size = fdt_totalsize(fdt); diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 50aaf15f9ad5..cc177152d0df 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -46,7 +46,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to open file: "); + efi_err("Failed to open file: "); efi_char16_printk(fi->filename); efi_printk("\n"); return status; @@ -55,7 +55,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, info_sz = sizeof(struct finfo); status = fh->get_info(fh, &info_guid, &info_sz, fi); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to get file info\n"); + efi_err("Failed to get file info\n"); fh->close(fh); return status; } @@ -75,13 +75,13 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image, status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, (void **)&io); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to handle fs_proto\n"); + efi_err("Failed to handle fs_proto\n"); return status; } status = io->open_volume(io, fh); if (status != EFI_SUCCESS) - pr_efi_err("Failed to open volume\n"); + efi_err("Failed to open volume\n"); return status; } @@ -191,7 +191,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, &alloc_addr, hard_limit); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate memory for files\n"); + efi_err("Failed to allocate memory for files\n"); goto err_close_file; } @@ -215,7 +215,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, status = file->read(file, &chunksize, addr); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to read file\n"); + efi_err("Failed to read file\n"); goto err_close_file; } addr += chunksize; diff --git a/drivers/firmware/efi/libstub/pci.c b/drivers/firmware/efi/libstub/pci.c index b025e59b94df..60af51bed573 100644 --- a/drivers/firmware/efi/libstub/pci.c +++ b/drivers/firmware/efi/libstub/pci.c @@ -28,21 +28,21 @@ void efi_pci_disable_bridge_busmaster(void) if (status != EFI_BUFFER_TOO_SMALL) { if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) - pr_efi_err("Failed to locate PCI I/O handles'\n"); + efi_err("Failed to locate PCI I/O handles'\n"); return; } status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, pci_handle_size, (void **)&pci_handle); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate memory for 'pci_handle'\n"); + efi_err("Failed to allocate memory for 'pci_handle'\n"); return; } status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, &pci_proto, NULL, &pci_handle_size, pci_handle); if (status != EFI_SUCCESS) { - pr_efi_err("Failed to locate PCI I/O handles'\n"); + efi_err("Failed to locate PCI I/O handles'\n"); goto free_handle; } @@ -106,7 +106,7 @@ void efi_pci_disable_bridge_busmaster(void) status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16, PCI_COMMAND, 1, &command); if (status != EFI_SUCCESS) - pr_efi_err("Failed to disable PCI busmastering\n"); + efi_err("Failed to disable PCI busmastering\n"); } free_handle: diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c index 1507d3c82884..93c04d6aaed1 100644 --- a/drivers/firmware/efi/libstub/relocate.c +++ b/drivers/firmware/efi/libstub/relocate.c @@ -157,7 +157,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, min_addr); } if (status != EFI_SUCCESS) { - pr_efi_err("Failed to allocate usable memory for kernel.\n"); + efi_err("Failed to allocate usable memory for kernel.\n"); return status; } diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c index a765378ad18c..5efc524b14be 100644 --- a/drivers/firmware/efi/libstub/secureboot.c +++ b/drivers/firmware/efi/libstub/secureboot.c @@ -67,10 +67,10 @@ enum efi_secureboot_mode efi_get_secureboot(void) return efi_secureboot_mode_disabled; secure_boot_enabled: - pr_efi("UEFI Secure Boot is enabled.\n"); + efi_info("UEFI Secure Boot is enabled.\n"); return efi_secureboot_mode_enabled; out_efi_err: - pr_efi_err("Could not determine UEFI Secure Boot status.\n"); + efi_err("Could not determine UEFI Secure Boot status.\n"); return efi_secureboot_mode_unknown; } -- cgit v1.2.3-70-g09d2 From 36bdd0a78d56831f5d8df740012b6cdc71c0c73d Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:36 -0400 Subject: efi/x86: Use efi_err for error messages Use efi_err instead of bare efi_printk for error messages. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-5-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/x86-stub.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index f91d4aab0156..3800eb22232e 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -49,7 +49,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, (void **)&rom); if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate memory for 'rom'\n"); + efi_err("Failed to allocate memory for 'rom'\n"); return status; } @@ -65,7 +65,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) PCI_VENDOR_ID, 1, &rom->vendor); if (status != EFI_SUCCESS) { - efi_printk("Failed to read rom->vendor\n"); + efi_err("Failed to read rom->vendor\n"); goto free_struct; } @@ -73,7 +73,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) PCI_DEVICE_ID, 1, &rom->devid); if (status != EFI_SUCCESS) { - efi_printk("Failed to read rom->devid\n"); + efi_err("Failed to read rom->devid\n"); goto free_struct; } @@ -118,7 +118,7 @@ static void setup_efi_pci(struct boot_params *params) (void **)&pci_handle); if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate memory for 'pci_handle'\n"); + efi_err("Failed to allocate memory for 'pci_handle'\n"); return; } @@ -172,7 +172,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) return; if (efi_table_attr(p, version) != 0x10000) { - efi_printk("Unsupported properties proto version\n"); + efi_err("Unsupported properties proto version\n"); return; } @@ -185,7 +185,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params) size + sizeof(struct setup_data), (void **)&new); if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate memory for 'properties'\n"); + efi_err("Failed to allocate memory for 'properties'\n"); return; } @@ -372,7 +372,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, status = efi_bs_call(handle_protocol, handle, &proto, (void **)&image); if (status != EFI_SUCCESS) { - efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); + efi_err("Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); efi_exit(handle, status); } @@ -382,7 +382,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, status = efi_allocate_pages(sizeof(struct boot_params), (unsigned long *)&boot_params, ULONG_MAX); if (status != EFI_SUCCESS) { - efi_printk("Failed to allocate lowmem for boot params\n"); + efi_err("Failed to allocate lowmem for boot params\n"); efi_exit(handle, status); } @@ -749,7 +749,7 @@ unsigned long efi_main(efi_handle_t handle, hdr->kernel_alignment, LOAD_PHYSICAL_ADDR); if (status != EFI_SUCCESS) { - efi_printk("efi_relocate_kernel() failed!\n"); + efi_err("efi_relocate_kernel() failed!\n"); goto fail; } /* @@ -786,7 +786,7 @@ unsigned long efi_main(efi_handle_t handle, efi_set_u64_split(size, &hdr->ramdisk_size, &boot_params->ext_ramdisk_size); } else if (status != EFI_NOT_FOUND) { - efi_printk("efi_load_initrd_dev_path() failed!\n"); + efi_err("efi_load_initrd_dev_path() failed!\n"); goto fail; } } @@ -813,13 +813,13 @@ unsigned long efi_main(efi_handle_t handle, status = exit_boot(boot_params, handle); if (status != EFI_SUCCESS) { - efi_printk("exit_boot() failed!\n"); + efi_err("exit_boot() failed!\n"); goto fail; } return bzimage_addr; fail: - efi_printk("efi_main() failed!\n"); + efi_err("efi_main() failed!\n"); efi_exit(handle, status); } -- cgit v1.2.3-70-g09d2 From 61eac6d92d351fc446c7a0ba3b55a9b8d9977e30 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:37 -0400 Subject: efi/gop: Use efi_err for error messages Use efi_err instead of bare efi_printk for error messages. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-6-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/gop.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 64cee0febae0..34c0cba2c8bf 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -134,14 +134,14 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) max_mode = efi_table_attr(mode, max_mode); if (cmdline.mode >= max_mode) { - efi_printk("Requested mode is invalid\n"); + efi_err("Requested mode is invalid\n"); return cur_mode; } status = efi_call_proto(gop, query_mode, cmdline.mode, &info_size, &info); if (status != EFI_SUCCESS) { - efi_printk("Couldn't get mode information\n"); + efi_err("Couldn't get mode information\n"); return cur_mode; } @@ -150,7 +150,7 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) efi_bs_call(free_pool, info); if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { - efi_printk("Invalid PixelFormat\n"); + efi_err("Invalid PixelFormat\n"); return cur_mode; } @@ -222,7 +222,7 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) return m; } - efi_printk("Couldn't find requested mode\n"); + efi_err("Couldn't find requested mode\n"); return cur_mode; } @@ -316,7 +316,7 @@ static void set_mode(efi_graphics_output_protocol_t *gop) return; if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) - efi_printk("Failed to set requested mode\n"); + efi_err("Failed to set requested mode\n"); } static void find_bits(u32 mask, u8 *pos, u8 *size) -- cgit v1.2.3-70-g09d2 From dad5b324212ef261e9b399b7456a4c3df0a8c300 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:38 -0400 Subject: efi/tpm: Use efi_err for error messages Use efi_err instead of bare efi_printk for error messages. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-7-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c index 1d59e103a2e3..09adcf51b75b 100644 --- a/drivers/firmware/efi/libstub/tpm.c +++ b/drivers/firmware/efi/libstub/tpm.c @@ -119,7 +119,7 @@ void efi_retrieve_tpm2_eventlog(void) sizeof(*log_tbl) + log_size, (void **)&log_tbl); if (status != EFI_SUCCESS) { - efi_printk("Unable to allocate memory for event log\n"); + efi_err("Unable to allocate memory for event log\n"); return; } -- cgit v1.2.3-70-g09d2 From 3839ab85d0fabd8b51c7548c4c1bc35bdba8adca Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:39 -0400 Subject: efi/libstub: Upgrade ignored dtb= argument message to error Use efi_err if we ignore a command-line dtb= argument, so that it shows up even on a quiet boot. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-8-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 72ffd2670f99..cb02e8bb6b44 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -241,7 +241,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) || secure_boot != efi_secureboot_mode_disabled) { if (strstr(cmdline_ptr, "dtb=")) - efi_info("Ignoring DTB from command line.\n"); + efi_err("Ignoring DTB from command line.\n"); } else { status = efi_load_dtb(image, &fdt_addr, &fdt_size); -- cgit v1.2.3-70-g09d2 From 987053a30016a7d9ab3e4ad973e7c51aeb1f1ef6 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:40 -0400 Subject: efi/x86: Move command-line initrd loading to efi_main Consolidate the initrd loading in efi_main. The command line options now need to be parsed only once. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-9-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/x86-stub.c | 64 +++++++++++++-------------------- 1 file changed, 25 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 3800eb22232e..defeb6035109 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -22,6 +22,7 @@ const efi_system_table_t *efi_system_table; extern u32 image_offset; +static efi_loaded_image_t *image = NULL; static efi_status_t preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom) @@ -355,7 +356,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, { struct boot_params *boot_params; struct setup_header *hdr; - efi_loaded_image_t *image; void *image_base; efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; int options_size = 0; @@ -414,30 +414,9 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr->ramdisk_image = 0; hdr->ramdisk_size = 0; - if (efi_is_native()) { - status = efi_parse_options(cmdline_ptr); - if (status != EFI_SUCCESS) - goto fail2; - - if (!efi_noinitrd) { - status = efi_load_initrd(image, &ramdisk_addr, - &ramdisk_size, - hdr->initrd_addr_max, - ULONG_MAX); - if (status != EFI_SUCCESS) - goto fail2; - efi_set_u64_split(ramdisk_addr, &hdr->ramdisk_image, - &boot_params->ext_ramdisk_image); - efi_set_u64_split(ramdisk_size, &hdr->ramdisk_size, - &boot_params->ext_ramdisk_size); - } - } - efi_stub_entry(handle, sys_table_arg, boot_params); /* not reached */ -fail2: - efi_free(options_size, (unsigned long)cmdline_ptr); fail: efi_free(sizeof(struct boot_params), (unsigned long)boot_params); @@ -760,35 +739,42 @@ unsigned long efi_main(efi_handle_t handle, image_offset = 0; } - /* - * efi_pe_entry() may have been called before efi_main(), in which - * case this is the second time we parse the cmdline. This is ok, - * parsing the cmdline multiple times does not have side-effects. - */ cmdline_paddr = ((u64)hdr->cmd_line_ptr | ((u64)boot_params->ext_cmd_line_ptr << 32)); efi_parse_options((char *)cmdline_paddr); /* - * At this point, an initrd may already have been loaded, either by - * the bootloader and passed via bootparams, or loaded from a initrd= - * command line option by efi_pe_entry() above. In either case, we - * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device - * path to supersede it. + * At this point, an initrd may already have been loaded by the + * bootloader and passed via bootparams. We permit an initrd loaded + * from the LINUX_EFI_INITRD_MEDIA_GUID device path to supersede it. + * + * If the device path is not present, any command-line initrd= + * arguments will be processed only if image is not NULL, which will be + * the case only if we were loaded via the PE entry point. */ if (!efi_noinitrd) { unsigned long addr, size; status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX); - if (status == EFI_SUCCESS) { - efi_set_u64_split(addr, &hdr->ramdisk_image, - &boot_params->ext_ramdisk_image); - efi_set_u64_split(size, &hdr->ramdisk_size, - &boot_params->ext_ramdisk_size); - } else if (status != EFI_NOT_FOUND) { - efi_err("efi_load_initrd_dev_path() failed!\n"); + if (status == EFI_NOT_FOUND) { + if (efi_is_native() && image != NULL) { + status = efi_load_initrd(image, &addr, &size, + hdr->initrd_addr_max, + ULONG_MAX); + } else { + addr = size = 0; + status = EFI_SUCCESS; + } + } + + if (status != EFI_SUCCESS) { + efi_err("Failed to load initrd!\n"); goto fail; } + efi_set_u64_split(addr, &hdr->ramdisk_image, + &boot_params->ext_ramdisk_image); + efi_set_u64_split(size, &hdr->ramdisk_size, + &boot_params->ext_ramdisk_size); } /* -- cgit v1.2.3-70-g09d2 From f61900fd0ebf6c6b91719d63272a54f4d11051df Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:41 -0400 Subject: efi/libstub: Unify initrd loading across architectures Factor out the initrd loading into a common function that can be called both from the generic efi-stub.c and the x86-specific x86-stub.c. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-10-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 46 ++++++++++++++++++++++++-- drivers/firmware/efi/libstub/efi-stub.c | 12 ++----- drivers/firmware/efi/libstub/efistub.h | 21 +++--------- drivers/firmware/efi/libstub/x86-stub.c | 13 ++------ 4 files changed, 52 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 1c92ac231f94..7aac89e928ec 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -331,6 +331,7 @@ static const struct { * %EFI_OUT_OF_RESOURCES if memory allocation failed * %EFI_LOAD_ERROR in all other cases */ +static efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, unsigned long *load_size, unsigned long max) @@ -343,9 +344,6 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, efi_handle_t handle; efi_status_t status; - if (!load_addr || !load_size) - return EFI_INVALID_PARAMETER; - dp = (efi_device_path_protocol_t *)&initrd_dev_path; status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); if (status != EFI_SUCCESS) @@ -375,3 +373,45 @@ efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, *load_size = initrd_size; return EFI_SUCCESS; } + +static +efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long soft_limit, + unsigned long hard_limit) +{ + if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || + (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) { + *load_addr = *load_size = 0; + return EFI_SUCCESS; + } + + return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, + soft_limit, hard_limit, + load_addr, load_size); +} + +efi_status_t efi_load_initrd(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long soft_limit, + unsigned long hard_limit) +{ + efi_status_t status; + + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + status = efi_load_initrd_dev_path(load_addr, load_size, hard_limit); + if (status == EFI_SUCCESS) { + efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + } else if (status == EFI_NOT_FOUND) { + status = efi_load_initrd_cmdline(image, load_addr, load_size, + soft_limit, hard_limit); + if (status == EFI_SUCCESS && *load_size > 0) + efi_info("Loaded initrd from command line option\n"); + } + + return status; +} diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index cb02e8bb6b44..63541c2440ef 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -265,16 +265,8 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!efi_noinitrd) { max_addr = efi_get_max_initrd_addr(dram_base, image_addr); - status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, - max_addr); - if (status == EFI_SUCCESS) { - efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); - } else if (status == EFI_NOT_FOUND) { - status = efi_load_initrd(image, &initrd_addr, &initrd_size, - ULONG_MAX, max_addr); - if (status == EFI_SUCCESS && initrd_size > 0) - efi_info("Loaded initrd from command line option\n"); - } + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + ULONG_MAX, max_addr); if (status != EFI_SUCCESS) efi_err("Failed to load initrd!\n"); } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 8c905a1be1b4..874233cf8820 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -677,21 +677,10 @@ static inline efi_status_t efi_load_dtb(efi_loaded_image_t *image, ULONG_MAX, ULONG_MAX, load_addr, load_size); } -static inline efi_status_t efi_load_initrd(efi_loaded_image_t *image, - unsigned long *load_addr, - unsigned long *load_size, - unsigned long soft_limit, - unsigned long hard_limit) -{ - if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER)) - return EFI_SUCCESS; - - return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, - soft_limit, hard_limit, load_addr, load_size); -} - -efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, - unsigned long *load_size, - unsigned long max); +efi_status_t efi_load_initrd(efi_loaded_image_t *image, + unsigned long *load_addr, + unsigned long *load_size, + unsigned long soft_limit, + unsigned long hard_limit); #endif diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index defeb6035109..f1a134596b53 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -755,17 +755,8 @@ unsigned long efi_main(efi_handle_t handle, if (!efi_noinitrd) { unsigned long addr, size; - status = efi_load_initrd_dev_path(&addr, &size, ULONG_MAX); - if (status == EFI_NOT_FOUND) { - if (efi_is_native() && image != NULL) { - status = efi_load_initrd(image, &addr, &size, - hdr->initrd_addr_max, - ULONG_MAX); - } else { - addr = size = 0; - status = EFI_SUCCESS; - } - } + status = efi_load_initrd(image, &addr, &size, + hdr->initrd_addr_max, ULONG_MAX); if (status != EFI_SUCCESS) { efi_err("Failed to load initrd!\n"); -- cgit v1.2.3-70-g09d2 From 7dde67f2a23ac11962b17128d9a71698430ced95 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:42 -0400 Subject: efi/x86: Support builtin command line Add support for the x86 CMDLINE_BOOL and CMDLINE_OVERRIDE configuration options. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-11-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/x86-stub.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index f1a134596b53..c84c5678e3e1 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -680,7 +680,6 @@ unsigned long efi_main(efi_handle_t handle, unsigned long buffer_start, buffer_end; struct setup_header *hdr = &boot_params->hdr; efi_status_t status; - unsigned long cmdline_paddr; efi_system_table = sys_table_arg; @@ -739,9 +738,14 @@ unsigned long efi_main(efi_handle_t handle, image_offset = 0; } - cmdline_paddr = ((u64)hdr->cmd_line_ptr | - ((u64)boot_params->ext_cmd_line_ptr << 32)); - efi_parse_options((char *)cmdline_paddr); +#ifdef CONFIG_CMDLINE_BOOL + efi_parse_options(CONFIG_CMDLINE); +#endif + if (!IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) { + unsigned long cmdline_paddr = ((u64)hdr->cmd_line_ptr | + ((u64)boot_params->ext_cmd_line_ptr << 32)); + efi_parse_options((char *)cmdline_paddr); + } /* * At this point, an initrd may already have been loaded by the -- cgit v1.2.3-70-g09d2 From 055042bedd4e0af8186266b189b81e73e708b82b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:43 -0400 Subject: efi/libstub: Check return value of efi_parse_options efi_parse_options can fail if it is unable to allocate space for a copy of the command line. Check the return value to make sure it succeeded. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200430182843.2510180-12-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub.c | 23 +++++++++++++++++------ drivers/firmware/efi/libstub/x86-stub.c | 12 ++++++++++-- 2 files changed, 27 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 63541c2440ef..c2484bf75c5d 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -207,11 +207,21 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || IS_ENABLED(CONFIG_CMDLINE_FORCE) || - cmdline_size == 0) - efi_parse_options(CONFIG_CMDLINE); + cmdline_size == 0) { + status = efi_parse_options(CONFIG_CMDLINE); + if (status != EFI_SUCCESS) { + efi_err("Failed to parse options\n"); + goto fail_free_cmdline; + } + } - if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) - efi_parse_options(cmdline_ptr); + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) { + status = efi_parse_options(cmdline_ptr); + if (status != EFI_SUCCESS) { + efi_err("Failed to parse options\n"); + goto fail_free_cmdline; + } + } efi_info("Booting Linux Kernel...\n"); @@ -223,7 +233,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) dram_base, image); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); - goto fail_free_cmdline; + goto fail_free_screeninfo; } efi_retrieve_tpm2_eventlog(); @@ -326,8 +336,9 @@ fail_free_initrd: fail_free_image: efi_free(image_size, image_addr); efi_free(reserve_size, reserve_addr); -fail_free_cmdline: +fail_free_screeninfo: free_screen_info(si); +fail_free_cmdline: efi_free(cmdline_size, (unsigned long)cmdline_ptr); fail: return status; diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index c84c5678e3e1..37154bb93c59 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -739,12 +739,20 @@ unsigned long efi_main(efi_handle_t handle, } #ifdef CONFIG_CMDLINE_BOOL - efi_parse_options(CONFIG_CMDLINE); + status = efi_parse_options(CONFIG_CMDLINE); + if (status != EFI_SUCCESS) { + efi_err("Failed to parse options\n"); + goto fail; + } #endif if (!IS_ENABLED(CONFIG_CMDLINE_OVERRIDE)) { unsigned long cmdline_paddr = ((u64)hdr->cmd_line_ptr | ((u64)boot_params->ext_cmd_line_ptr << 32)); - efi_parse_options((char *)cmdline_paddr); + status = efi_parse_options((char *)cmdline_paddr); + if (status != EFI_SUCCESS) { + efi_err("Failed to parse options\n"); + goto fail; + } } /* -- cgit v1.2.3-70-g09d2 From de8c55208c3865d0532466097b0244fbea1d9089 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 4 May 2020 11:02:48 -0400 Subject: efi/libstub: Fix mixed mode boot issue after macro refactor Commit 22090f84bc3f ("efi/libstub: unify EFI call wrappers for non-x86") refactored the macros that are used to provide wrappers for mixed-mode calls on x86, allowing us to boot a 64-bit kernel on 32-bit firmware. Unfortunately, this broke mixed mode boot due to the fact that efi_is_native() is not a macro on x86. All of these macros should go together, so rather than testing each one to see if it is defined, condition the generic macro definitions on a new ARCH_HAS_EFISTUB_WRAPPERS, and remove the wrapper definitions on x86 as well if CONFIG_EFI_MIXED is not enabled. Fixes: 22090f84bc3f ("efi/libstub: unify EFI call wrappers for non-x86") Reported-by: Guenter Roeck Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200504150248.62482-1-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 19 +++++++++++++++---- drivers/firmware/efi/libstub/efistub.h | 14 ++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index cd0c3fbf6156..6b9ab0d8b2a7 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -225,13 +225,15 @@ efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, /* arch specific definitions used by the stub code */ -extern const bool efi_is64; +#ifdef CONFIG_EFI_MIXED + +#define ARCH_HAS_EFISTUB_WRAPPERS static inline bool efi_is_64bit(void) { - if (IS_ENABLED(CONFIG_EFI_MIXED)) - return efi_is64; - return IS_ENABLED(CONFIG_X86_64); + extern const bool efi_is64; + + return efi_is64; } static inline bool efi_is_native(void) @@ -356,6 +358,15 @@ static inline u32 efi64_convert_status(efi_status_t status) runtime), \ func, __VA_ARGS__)) +#else /* CONFIG_EFI_MIXED */ + +static inline bool efi_is_64bit(void) +{ + return IS_ENABLED(CONFIG_X86_64); +} + +#endif /* CONFIG_EFI_MIXED */ + extern bool efi_reboot_required(void); extern bool efi_is_table_address(unsigned long phys_addr); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 874233cf8820..4f10a09563f3 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -33,20 +33,14 @@ extern bool efi_novamap; extern const efi_system_table_t *efi_system_table; -#ifndef efi_bs_call +#ifndef ARCH_HAS_EFISTUB_WRAPPERS + +#define efi_is_native() (true) #define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__) -#endif -#ifndef efi_rt_call #define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__) -#endif -#ifndef efi_is_native -#define efi_is_native() (true) -#endif -#ifndef efi_table_attr #define efi_table_attr(inst, attr) (inst->attr) -#endif -#ifndef efi_call_proto #define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) + #endif #define efi_info(msg) do { \ -- cgit v1.2.3-70-g09d2 From f77767ed5f4d398b29119563155e4ece2dfeee13 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 4 May 2020 10:06:29 +0200 Subject: efi/libstub/x86: Work around LLVM ELF quirk build regression When building the x86 EFI stub with Clang, the libstub Makefile rules that manipulate the ELF object files may throw an error like: STUBCPY drivers/firmware/efi/libstub/efi-stub-helper.stub.o strip: drivers/firmware/efi/libstub/efi-stub-helper.stub.o: Failed to find link section for section 10 objcopy: drivers/firmware/efi/libstub/efi-stub-helper.stub.o: Failed to find link section for section 10 This is the result of a LLVM feature [0] where symbol references are stored in a LLVM specific .llvm_addrsig section in a non-transparent way, causing generic ELF tools such as strip or objcopy to choke on them. So force the compiler not to emit these sections, by passing the appropriate command line option. [0] https://sourceware.org/bugzilla/show_bug.cgi?id=23817 Cc: Nick Desaulniers Cc: Peter Collingbourne Cc: Sami Tolvanen Reported-by: Arnd Bergmann Suggested-by: Fangrui Song Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 8d246b51bd49..e5a49dc8e9bc 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -30,6 +30,7 @@ KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ -D__NO_FORTIFY \ $(call cc-option,-ffreestanding) \ $(call cc-option,-fno-stack-protector) \ + $(call cc-option,-fno-addrsig) \ -D__DISABLE_EXPORTS GCOV_PROFILE := n -- cgit v1.2.3-70-g09d2 From 0b8d9fc9953fde4ea3eb3191d986ca2d01eb783c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 5 May 2020 10:27:18 +0200 Subject: efi/libstub: Make efi_printk() input argument const char* To help the compiler figure out that efi_printk() will not modify the string it is given, make the input argument type const char*. While at it, simplify the implementation as well. Suggested-by: Joe Perches Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 19 +++++++------------ drivers/firmware/efi/libstub/efistub.h | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 7aac89e928ec..2927f3d30344 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -26,20 +26,15 @@ bool __pure __efi_soft_reserve_enabled(void) return !efi_nosoftreserve; } -void efi_printk(char *str) +void efi_printk(const 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); - } + while (*str) { + efi_char16_t ch[] = { *str++, L'\0' }; - efi_char16_printk(ch); + if (ch[0] == L'\n') + efi_char16_printk(L"\r\n"); + else + efi_char16_printk(ch); } } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 4f10a09563f3..15d0b6f3f6c6 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -625,7 +625,7 @@ efi_status_t check_platform_features(void); void *get_efi_config_table(efi_guid_t guid); -void efi_printk(char *str); +void efi_printk(const char *str); void efi_free(unsigned long size, unsigned long addr); -- cgit v1.2.3-70-g09d2 From 4026229934f6ca0cb44af7b9df00e647b2f1f787 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 30 Apr 2020 12:12:42 -0700 Subject: efi/libstub: Correct comment typos Fix a couple typos in comments. Signed-off-by: Joe Perches Link: https://lore.kernel.org/r/ec53e67b3ac928922807db3cb1585e911971dadc.1588273612.git.joe@perches.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/pci.c | 2 +- drivers/firmware/efi/libstub/relocate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/pci.c b/drivers/firmware/efi/libstub/pci.c index 60af51bed573..99fb25d2bcf5 100644 --- a/drivers/firmware/efi/libstub/pci.c +++ b/drivers/firmware/efi/libstub/pci.c @@ -69,7 +69,7 @@ void efi_pci_disable_bridge_busmaster(void) * access to the framebuffer. Drivers for true PCIe graphics * controllers that are behind a PCIe root port do not use * DMA to implement the GOP framebuffer anyway [although they - * may use it in their implentation of Gop->Blt()], and so + * may use it in their implementation of Gop->Blt()], and so * disabling DMA in the PCI bridge should not interfere with * normal operation of the device. */ diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c index 93c04d6aaed1..9b1aaf8b123f 100644 --- a/drivers/firmware/efi/libstub/relocate.c +++ b/drivers/firmware/efi/libstub/relocate.c @@ -140,7 +140,7 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr, * The EFI firmware loader could have placed the kernel image * 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 + * also have a preferred address, so first try to relocate * to the preferred address. If that fails, allocate as low * as possible while respecting the required alignment. */ -- cgit v1.2.3-70-g09d2 From 0b76735380224497e1c78a1bed606cbda83e3427 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:53 -0400 Subject: efi/libstub: Include dependencies of efistub.h Add #include directives for include files that efistub.h depends on, instead of relying on them having been included by the C source files prior to efistub.h. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 15d0b6f3f6c6..998924916b03 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -3,6 +3,11 @@ #ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H #define _DRIVERS_FIRMWARE_EFI_EFISTUB_H +#include +#include +#include +#include + /* error code which can't be mistaken for valid address */ #define EFI_ERROR (~0UL) -- cgit v1.2.3-70-g09d2 From cb8c90a08c7f2f761919190093fca0611c1cece4 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:54 -0400 Subject: efi/libstub: Rename efi_[char16_]printk to efi_[char16_]puts These functions do not support formatting, unlike printk. Rename them to puts to make that clear. Move the implementations of these two functions next to each other. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-3-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 18 +++++++++--------- drivers/firmware/efi/libstub/efistub.h | 10 +++++----- drivers/firmware/efi/libstub/file.c | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 2927f3d30344..c6d7ef35e9f7 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -26,15 +26,21 @@ bool __pure __efi_soft_reserve_enabled(void) return !efi_nosoftreserve; } -void efi_printk(const char *str) +void efi_char16_puts(efi_char16_t *str) +{ + efi_call_proto(efi_table_attr(efi_system_table, con_out), + output_string, str); +} + +void efi_puts(const char *str) { while (*str) { efi_char16_t ch[] = { *str++, L'\0' }; if (ch[0] == L'\n') - efi_char16_printk(L"\r\n"); + efi_char16_puts(L"\r\n"); else - efi_char16_printk(ch); + efi_char16_puts(ch); } } @@ -279,12 +285,6 @@ void *get_efi_config_table(efi_guid_t guid) return NULL; } -void efi_char16_printk(efi_char16_t *str) -{ - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, str); -} - /* * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way * for the firmware or bootloader to expose the initrd data directly to the stub diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 998924916b03..4f57611a65f2 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -49,10 +49,10 @@ extern const efi_system_table_t *efi_system_table; #endif #define efi_info(msg) do { \ - if (!efi_quiet) efi_printk("EFI stub: "msg); \ + if (!efi_quiet) efi_puts("EFI stub: "msg); \ } while (0) -#define efi_err(msg) efi_printk("EFI stub: ERROR: "msg) +#define efi_err(msg) efi_puts("EFI stub: ERROR: "msg) /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var @@ -605,8 +605,6 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, efi_exit_boot_map_processing priv_func); -void efi_char16_printk(efi_char16_t *); - efi_status_t allocate_new_fdt_and_exit_boot(void *handle, unsigned long *new_fdt_addr, unsigned long max_addr, @@ -630,7 +628,9 @@ efi_status_t check_platform_features(void); void *get_efi_config_table(efi_guid_t guid); -void efi_printk(const char *str); +/* NOTE: These functions do not print a trailing newline after the string */ +void efi_char16_puts(efi_char16_t *); +void efi_puts(const char *str); void efi_free(unsigned long size, unsigned long addr); diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index cc177152d0df..933536c5236a 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -47,8 +47,8 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { efi_err("Failed to open file: "); - efi_char16_printk(fi->filename); - efi_printk("\n"); + efi_char16_puts(fi->filename); + efi_puts("\n"); return status; } -- cgit v1.2.3-70-g09d2 From fd0528a24958b7e8d55669cf50749776308d4c5a Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:55 -0400 Subject: efi/libstub: Buffer output of efi_puts Use a buffer to convert the string to UTF-16. This will reduce the number of firmware calls required to print the string from one per character to one per string in most cases. Cast the input char to unsigned char before converting to efi_char16_t to avoid sign-extension in case there are any non-ASCII characters in the input. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-4-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index c6d7ef35e9f7..3cf506ab9ead 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -8,6 +8,7 @@ */ #include +#include #include #include "efistub.h" @@ -34,13 +35,19 @@ void efi_char16_puts(efi_char16_t *str) void efi_puts(const char *str) { - while (*str) { - efi_char16_t ch[] = { *str++, L'\0' }; + efi_char16_t buf[128]; + size_t pos = 0, lim = ARRAY_SIZE(buf); - if (ch[0] == L'\n') - efi_char16_puts(L"\r\n"); - else - efi_char16_puts(ch); + while (*str) { + if (*str == '\n') + buf[pos++] = L'\r'; + /* Cast to unsigned char to avoid sign-extension */ + buf[pos++] = (unsigned char)(*str++); + if (*str == '\0' || pos >= lim - 2) { + buf[pos] = L'\0'; + efi_char16_puts(buf); + pos = 0; + } } } -- cgit v1.2.3-70-g09d2 From 2c7d1e30e5884dc6f6727ecd9417491c9f321b59 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:56 -0400 Subject: efi/libstub: Add a basic printf implementation Copy vsprintf from arch/x86/boot/printf.c to get a simple printf implementation. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-5-nivedita@alum.mit.edu [ardb: add some missing braces in if...else clauses] Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 17 ++ drivers/firmware/efi/libstub/efistub.h | 3 + drivers/firmware/efi/libstub/vsprintf.c | 299 +++++++++++++++++++++++++ 4 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/efi/libstub/vsprintf.c (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index e5a49dc8e9bc..fb34c9d14a3c 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -44,7 +44,7 @@ KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o relocate.o + alignedmem.o relocate.o vsprintf.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 3cf506ab9ead..56b3b84fd3bd 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -7,6 +7,8 @@ * Copyright 2011 Intel Corporation; author Matt Fleming */ +#include + #include #include #include @@ -51,6 +53,21 @@ void efi_puts(const char *str) } } +int efi_printk(const char *fmt, ...) +{ + char printf_buf[256]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + efi_puts(printf_buf); + + return printed; +} + /* * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= * option, e.g. efi=nochunk. diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 4f57611a65f2..caa7dcc71c69 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -3,6 +3,7 @@ #ifndef _DRIVERS_FIRMWARE_EFI_EFISTUB_H #define _DRIVERS_FIRMWARE_EFI_EFISTUB_H +#include #include #include #include @@ -632,6 +633,8 @@ void *get_efi_config_table(efi_guid_t guid); void efi_char16_puts(efi_char16_t *); void efi_puts(const char *str); +__printf(1, 2) int efi_printk(char const *fmt, ...); + void efi_free(unsigned long size, unsigned long addr); char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c new file mode 100644 index 000000000000..bb62d3726c65 --- /dev/null +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * ----------------------------------------------------------------------- */ + +/* + * Oh, it's a waste of space, but oh-so-yummy for debugging. This + * version of printf() does not include 64-bit support. "Live with + * it." + * + */ + +#include + +#include +#include +#include + +static int skip_atoi(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SMALL 32 /* Must be 32 == 0x20 */ +#define SPECIAL 64 /* 0x */ + +#define __do_div(n, base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char *number(char *str, long num, int base, int size, int precision, + int type) +{ + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + + char tmp[66]; + char c, sign, locase; + int i; + + /* locase = 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters */ + locase = (type & SMALL); + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 16) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++] = '0'; + else + while (num != 0) + tmp[i++] = (digits[__do_div(num, base)] | locase); + if (i > precision) + precision = i; + size -= precision; + if (!(type & (ZEROPAD + LEFT))) + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base == 8) { + *str++ = '0'; + } else if (base == 16) { + *str++ = '0'; + *str++ = ('X' | locase); + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char *str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str = buf; *fmt; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= SPECIAL; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char)va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long)va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int *ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'x': + flags |= SMALL; + fallthrough; + case 'X': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + fallthrough; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') { + num = va_arg(args, unsigned long); + } else if (qualifier == 'h') { + num = (unsigned short)va_arg(args, int); + if (flags & SIGN) + num = (short)num; + } else if (flags & SIGN) { + num = va_arg(args, int); + } else { + num = va_arg(args, unsigned int); + } + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str - buf; +} + +int sprintf(char *buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + return i; +} -- cgit v1.2.3-70-g09d2 From bbf8e8b0fe0401e1952c77088692fd273a9635a1 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:57 -0400 Subject: efi/libstub: Optimize for size instead of speed Reclaim the bloat from the addition of printf by optimizing the stub for size. With gcc 9, the text size of the stub is: ARCH before +printf -Os arm 35197 37889 34638 arm64 34883 38159 34479 i386 18571 21657 17025 x86_64 25677 29328 22144 Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-6-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index fb34c9d14a3c..034d71663b1e 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -7,7 +7,7 @@ # cflags-$(CONFIG_X86_32) := -march=i386 cflags-$(CONFIG_X86_64) := -mcmodel=small -cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ +cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ -fPIC -fno-strict-aliasing -mno-red-zone \ -mno-mmx -mno-sse -fshort-wchar \ -Wno-pointer-sign \ @@ -25,7 +25,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt -KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \ +KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \ -include $(srctree)/drivers/firmware/efi/libstub/hidden.h \ -D__NO_FORTIFY \ $(call cc-option,-ffreestanding) \ -- cgit v1.2.3-70-g09d2 From 29a2806653360f1ba0f7608ef64d65d441a575f7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:58 -0400 Subject: efi/printf: Drop %n format and L qualifier %n is unused and deprecated. The L qualifer is parsed but not actually implemented. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-7-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index bb62d3726c65..52cf22d0b6e5 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -127,7 +127,7 @@ int vsprintf(char *buf, const char *fmt, va_list args) int field_width; /* width of output field */ int precision; /* min. # of digits for integers; max number of chars for from string */ - int qualifier; /* 'h', 'l', or 'L' for integer fields */ + int qualifier; /* 'h' or 'l' for integer fields */ for (str = buf; *fmt; ++fmt) { if (*fmt != '%') { @@ -188,7 +188,7 @@ int vsprintf(char *buf, const char *fmt, va_list args) /* get the conversion qualifier */ qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + if (*fmt == 'h' || *fmt == 'l') { qualifier = *fmt; ++fmt; } @@ -229,16 +229,6 @@ int vsprintf(char *buf, const char *fmt, va_list args) field_width, precision, flags); continue; - case 'n': - if (qualifier == 'l') { - long *ip = va_arg(args, long *); - *ip = (str - buf); - } else { - int *ip = va_arg(args, int *); - *ip = (str - buf); - } - continue; - case '%': *str++ = '%'; continue; -- cgit v1.2.3-70-g09d2 From ce5e3f909fc0ed67e58367b9c56a54c20a51080b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:59 -0400 Subject: efi/printf: Add 64-bit and 8-bit integer support Support 'll' qualifier for long long by copying the decimal printing code from lib/vsprintf.c. For simplicity, the 32-bit code is used on 64-bit architectures as well. Support 'hh' qualifier for signed/unsigned char type integers. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-8-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 170 +++++++++++++++++++++++++++----- 1 file changed, 143 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 52cf22d0b6e5..8fd15e87d18c 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -7,10 +7,7 @@ * ----------------------------------------------------------------------- */ /* - * Oh, it's a waste of space, but oh-so-yummy for debugging. This - * version of printf() does not include 64-bit support. "Live with - * it." - * + * Oh, it's a waste of space, but oh-so-yummy for debugging. */ #include @@ -28,6 +25,86 @@ static int skip_atoi(const char **s) return i; } +/* + * put_dec_full4 handles numbers in the range 0 <= r < 10000. + * The multiplier 0xccd is round(2^15/10), and the approximation + * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. + */ +static +void put_dec_full4(char *buf, unsigned int r) +{ + int i; + + for (i = 0; i < 3; i++) { + unsigned int q = (r * 0xccd) >> 15; + *buf++ = '0' + (r - q * 10); + r = q; + } + *buf++ = '0' + r; +} + +/* put_dec is copied from lib/vsprintf.c with small modifications */ + +/* + * Call put_dec_full4 on x % 10000, return x / 10000. + * The approximation x/10000 == (x * 0x346DC5D7) >> 43 + * holds for all x < 1,128,869,999. The largest value this + * helper will ever be asked to convert is 1,125,520,955. + * (second call in the put_dec code, assuming n is all-ones). + */ +static +unsigned int put_dec_helper4(char *buf, unsigned int x) +{ + unsigned int q = (x * 0x346DC5D7ULL) >> 43; + + put_dec_full4(buf, x - q * 10000); + return q; +} + +/* Based on code by Douglas W. Jones found at + * + * (with permission from the author). + * Performs no 64-bit division and hence should be fast on 32-bit machines. + */ +static +int put_dec(char *buf, unsigned long long n) +{ + unsigned int d3, d2, d1, q, h; + char *p = buf; + + d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */ + h = (n >> 32); + d2 = (h ) & 0xffff; + d3 = (h >> 16); /* implicit "& 0xffff" */ + + /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0 + = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */ + q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff); + q = put_dec_helper4(p, q); + p += 4; + + q += 7671 * d3 + 9496 * d2 + 6 * d1; + q = put_dec_helper4(p, q); + p += 4; + + q += 4749 * d3 + 42 * d2; + q = put_dec_helper4(p, q); + p += 4; + + q += 281 * d3; + q = put_dec_helper4(p, q); + p += 4; + + put_dec_full4(p, q); + p += 4; + + /* strip off the extra 0's we printed */ + while (p > buf && p[-1] == '0') + --p; + + return p - buf; +} + #define ZEROPAD 1 /* pad with zero */ #define SIGN 2 /* unsigned/signed long */ #define PLUS 4 /* show plus */ @@ -36,13 +113,7 @@ static int skip_atoi(const char **s) #define SMALL 32 /* Must be 32 == 0x20 */ #define SPECIAL 64 /* 0x */ -#define __do_div(n, base) ({ \ -int __res; \ -__res = ((unsigned long) n) % (unsigned) base; \ -n = ((unsigned long) n) / (unsigned) base; \ -__res; }) - -static char *number(char *str, long num, int base, int size, int precision, +static char *number(char *str, long long num, int base, int size, int precision, int type) { /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ @@ -57,8 +128,6 @@ static char *number(char *str, long num, int base, int size, int precision, locase = (type & SMALL); if (type & LEFT) type &= ~ZEROPAD; - if (base < 2 || base > 16) - return NULL; c = (type & ZEROPAD) ? '0' : ' '; sign = 0; if (type & SIGN) { @@ -83,9 +152,28 @@ static char *number(char *str, long num, int base, int size, int precision, i = 0; if (num == 0) tmp[i++] = '0'; - else - while (num != 0) - tmp[i++] = (digits[__do_div(num, base)] | locase); + else { + switch (base) { + case 10: + i += put_dec(&tmp[i], num); + break; + case 8: + while (num != 0) { + tmp[i++] = '0' + (num & 07); + num = (unsigned long long)num >> 3; + } + break; + case 16: + while (num != 0) { + tmp[i++] = digits[num & 0xf] | locase; + num = (unsigned long long)num >> 4; + } + break; + default: + unreachable(); + } + } + if (i > precision) precision = i; size -= precision; @@ -117,7 +205,7 @@ static char *number(char *str, long num, int base, int size, int precision, int vsprintf(char *buf, const char *fmt, va_list args) { int len; - unsigned long num; + unsigned long long num; int i, base; char *str; const char *s; @@ -127,7 +215,7 @@ int vsprintf(char *buf, const char *fmt, va_list args) int field_width; /* width of output field */ int precision; /* min. # of digits for integers; max number of chars for from string */ - int qualifier; /* 'h' or 'l' for integer fields */ + int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */ for (str = buf; *fmt; ++fmt) { if (*fmt != '%') { @@ -191,6 +279,10 @@ int vsprintf(char *buf, const char *fmt, va_list args) if (*fmt == 'h' || *fmt == 'l') { qualifier = *fmt; ++fmt; + if (qualifier == *fmt) { + qualifier -= 'a'-'A'; + ++fmt; + } } /* default base */ @@ -260,16 +352,40 @@ int vsprintf(char *buf, const char *fmt, va_list args) --fmt; continue; } - if (qualifier == 'l') { - num = va_arg(args, unsigned long); - } else if (qualifier == 'h') { - num = (unsigned short)va_arg(args, int); - if (flags & SIGN) - num = (short)num; - } else if (flags & SIGN) { - num = va_arg(args, int); + if (flags & SIGN) { + switch (qualifier) { + case 'L': + num = va_arg(args, long long); + break; + case 'l': + num = va_arg(args, long); + break; + case 'h': + num = (short)va_arg(args, int); + break; + case 'H': + num = (signed char)va_arg(args, int); + break; + default: + num = va_arg(args, int); + } } else { - num = va_arg(args, unsigned int); + switch (qualifier) { + case 'L': + num = va_arg(args, unsigned long long); + break; + case 'l': + num = va_arg(args, unsigned long); + break; + case 'h': + num = (unsigned short)va_arg(args, int); + break; + case 'H': + num = (unsigned char)va_arg(args, int); + break; + default: + num = va_arg(args, unsigned int); + } } str = number(str, num, base, field_width, precision, flags); } -- cgit v1.2.3-70-g09d2 From 3b8350959cba0df4070741618e50c367319793bb Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:00 -0400 Subject: efi/printf: Factor out flags parsing and handle '%' earlier Move flags parsing code out into a helper function. The '%%' case can be handled up front: it is not allowed to have flags, width etc. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-9-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 56 ++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 8fd15e87d18c..fb9eb83f1728 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -202,6 +202,35 @@ static char *number(char *str, long long num, int base, int size, int precision, return str; } +static +int get_flags(const char **fmt) +{ + int flags = 0; + + do { + switch (**fmt) { + case '-': + flags |= LEFT; + break; + case '+': + flags |= PLUS; + break; + case ' ': + flags |= SPACE; + break; + case '#': + flags |= SPECIAL; + break; + case '0': + flags |= ZEROPAD; + break; + default: + return flags; + } + ++(*fmt); + } while (1); +} + int vsprintf(char *buf, const char *fmt, va_list args) { int len; @@ -218,32 +247,13 @@ int vsprintf(char *buf, const char *fmt, va_list args) int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */ for (str = buf; *fmt; ++fmt) { - if (*fmt != '%') { + if (*fmt != '%' || *++fmt == '%') { *str++ = *fmt; continue; } /* process flags */ - flags = 0; - repeat: - ++fmt; /* this also skips first '%' */ - switch (*fmt) { - case '-': - flags |= LEFT; - goto repeat; - case '+': - flags |= PLUS; - goto repeat; - case ' ': - flags |= SPACE; - goto repeat; - case '#': - flags |= SPECIAL; - goto repeat; - case '0': - flags |= ZEROPAD; - goto repeat; - } + flags = get_flags(&fmt); /* get field width */ field_width = -1; @@ -321,10 +331,6 @@ int vsprintf(char *buf, const char *fmt, va_list args) field_width, precision, flags); continue; - case '%': - *str++ = '%'; - continue; - /* integer number formats - set up the flags and "break" */ case 'o': base = 8; -- cgit v1.2.3-70-g09d2 From 77e48db04a02ebd00229281c26575979b0b465e0 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:01 -0400 Subject: efi/printf: Fix minor bug in precision handling A negative precision should be ignored completely, and the presence of a valid precision should turn off the 0 flag. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-10-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index fb9eb83f1728..00123d5f402f 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -279,9 +279,11 @@ int vsprintf(char *buf, const char *fmt, va_list args) ++fmt; /* it's the next argument */ precision = va_arg(args, int); - } - if (precision < 0) + } else { precision = 0; + } + if (precision >= 0) + flags &= ~ZEROPAD; } /* get the conversion qualifier */ -- cgit v1.2.3-70-g09d2 From 7c30fd79168aeb83d11260246d93f8a293052007 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:02 -0400 Subject: efi/printf: Merge 'p' with the integer formats Treat 'p' as a hexadecimal integer with precision equal to the number of digits in void *. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-11-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 00123d5f402f..b7316ab9f8b4 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -297,9 +297,6 @@ int vsprintf(char *buf, const char *fmt, va_list args) } } - /* default base */ - base = 10; - switch (*fmt) { case 'c': if (!(flags & LEFT)) @@ -323,21 +320,15 @@ int vsprintf(char *buf, const char *fmt, va_list args) *str++ = ' '; continue; - case 'p': - if (field_width == -1) { - field_width = 2 * sizeof(void *); - flags |= ZEROPAD; - } - str = number(str, - (unsigned long)va_arg(args, void *), 16, - field_width, precision, flags); - continue; - /* integer number formats - set up the flags and "break" */ case 'o': base = 8; break; + case 'p': + if (precision < 0) + precision = 2 * sizeof(void *); + fallthrough; case 'x': flags |= SMALL; fallthrough; @@ -350,6 +341,7 @@ int vsprintf(char *buf, const char *fmt, va_list args) flags |= SIGN; fallthrough; case 'u': + base = 10; break; default: @@ -360,7 +352,9 @@ int vsprintf(char *buf, const char *fmt, va_list args) --fmt; continue; } - if (flags & SIGN) { + if (*fmt == 'p') { + num = (unsigned long)va_arg(args, void *); + } else if (flags & SIGN) { switch (qualifier) { case 'L': num = va_arg(args, long long); -- cgit v1.2.3-70-g09d2 From 3fbcf75bb41a0ff02189f6e873ba4c5d7c54c6a9 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:03 -0400 Subject: efi/printf: Factor out width/precision parsing Factor out the width/precision parsing into a helper function. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-12-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 61 +++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index b7316ab9f8b4..0e6f2c1158fd 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -231,7 +231,20 @@ int get_flags(const char **fmt) } while (1); } -int vsprintf(char *buf, const char *fmt, va_list args) +static +int get_int(const char **fmt, va_list *ap) +{ + if (isdigit(**fmt)) + return skip_atoi(fmt); + if (**fmt == '*') { + ++(*fmt); + /* it's the next argument */ + return va_arg(*ap, int); + } + return 0; +} + +int vsprintf(char *buf, const char *fmt, va_list ap) { int len; unsigned long long num; @@ -246,6 +259,24 @@ int vsprintf(char *buf, const char *fmt, va_list args) number of chars for from string */ int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */ + va_list args; + + /* + * We want to pass our input va_list to helper functions by reference, + * but there's an annoying edge case. If va_list was originally passed + * to us by value, we could just pass &ap down to the helpers. This is + * the case on, for example, X86_32. + * However, on X86_64 (and possibly others), va_list is actually a + * size-1 array containing a structure. Our function parameter ap has + * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], + * which is what will be expected by a function taking a va_list * + * parameter. + * One standard way to solve this mess is by creating a copy in a local + * variable of type va_list and then passing a pointer to that local + * copy instead, which is what we do here. + */ + va_copy(args, ap); + for (str = buf; *fmt; ++fmt) { if (*fmt != '%' || *++fmt == '%') { *str++ = *fmt; @@ -256,32 +287,17 @@ int vsprintf(char *buf, const char *fmt, va_list args) flags = get_flags(&fmt); /* get field width */ - field_width = -1; - if (isdigit(*fmt)) { - field_width = skip_atoi(&fmt); - } else if (*fmt == '*') { - ++fmt; - /* it's the next argument */ - field_width = va_arg(args, int); - if (field_width < 0) { - field_width = -field_width; - flags |= LEFT; - } + field_width = get_int(&fmt, &args); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; } /* get the precision */ precision = -1; if (*fmt == '.') { ++fmt; - if (isdigit(*fmt)) { - precision = skip_atoi(&fmt); - } else if (*fmt == '*') { - ++fmt; - /* it's the next argument */ - precision = va_arg(args, int); - } else { - precision = 0; - } + precision = get_int(&fmt, &args); if (precision >= 0) flags &= ~ZEROPAD; } @@ -392,6 +408,9 @@ int vsprintf(char *buf, const char *fmt, va_list args) str = number(str, num, base, field_width, precision, flags); } *str = '\0'; + + va_end(args); + return str - buf; } -- cgit v1.2.3-70-g09d2 From dec6119952eab7257624de5bd116a9e301ead5b7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:04 -0400 Subject: efi/printf: Factor out integer argument retrieval Factor out the code to get the correct type of numeric argument into a helper function. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-13-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 0e6f2c1158fd..27685c726c64 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -244,6 +244,38 @@ int get_int(const char **fmt, va_list *ap) return 0; } +static +unsigned long long get_number(int sign, int qualifier, va_list *ap) +{ + if (sign) { + switch (qualifier) { + case 'L': + return va_arg(*ap, long long); + case 'l': + return va_arg(*ap, long); + case 'h': + return (short)va_arg(*ap, int); + case 'H': + return (signed char)va_arg(*ap, int); + default: + return va_arg(*ap, int); + }; + } else { + switch (qualifier) { + case 'L': + return va_arg(*ap, unsigned long long); + case 'l': + return va_arg(*ap, unsigned long); + case 'h': + return (unsigned short)va_arg(*ap, int); + case 'H': + return (unsigned char)va_arg(*ap, int); + default: + return va_arg(*ap, unsigned int); + } + } +} + int vsprintf(char *buf, const char *fmt, va_list ap) { int len; @@ -370,40 +402,8 @@ int vsprintf(char *buf, const char *fmt, va_list ap) } if (*fmt == 'p') { num = (unsigned long)va_arg(args, void *); - } else if (flags & SIGN) { - switch (qualifier) { - case 'L': - num = va_arg(args, long long); - break; - case 'l': - num = va_arg(args, long); - break; - case 'h': - num = (short)va_arg(args, int); - break; - case 'H': - num = (signed char)va_arg(args, int); - break; - default: - num = va_arg(args, int); - } } else { - switch (qualifier) { - case 'L': - num = va_arg(args, unsigned long long); - break; - case 'l': - num = va_arg(args, unsigned long); - break; - case 'h': - num = (unsigned short)va_arg(args, int); - break; - case 'H': - num = (unsigned char)va_arg(args, int); - break; - default: - num = va_arg(args, unsigned int); - } + num = get_number(flags & SIGN, qualifier, &args); } str = number(str, num, base, field_width, precision, flags); } -- cgit v1.2.3-70-g09d2 From fb031937a86874e6d663542bdbd83e310c13610e Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:05 -0400 Subject: efi/printf: Handle null string input Print "(null)" for 's' if the input is a NULL pointer. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-14-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index 27685c726c64..d427a7b1ef64 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -14,6 +14,7 @@ #include #include +#include #include static int skip_atoi(const char **s) @@ -356,7 +357,11 @@ int vsprintf(char *buf, const char *fmt, va_list ap) continue; case 's': + if (precision < 0) + precision = INT_MAX; s = va_arg(args, char *); + if (!s) + s = precision < 6 ? "" : "(null)"; len = strnlen(s, precision); if (!(flags & LEFT)) -- cgit v1.2.3-70-g09d2 From 6c4bcd8a46a98856c06ca3ba8a80f03a61e23960 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:06 -0400 Subject: efi/printf: Refactor code to consolidate padding and output Consolidate the actual output of the formatted text into one place. Fix a couple of edge cases: 1. If 0 is printed with a precision of 0, the printf specification says that nothing should be output, with one exception (2b). 2. The specification for octal alternate format (%#o) adds the leading zero not as a prefix as the 0x for hexadecimal is, but by increasing the precision if necessary to add the zero. This means that a. %#.2o turns 8 into "010", but 1 into "01" rather than "001". b. %#.o prints 0 as "0" rather than "", unlike the situation for decimal, hexadecimal and regular octal format, which all output an empty string. Reduce the space allocated for printing a number to the maximum actually required (22 bytes for a 64-bit number in octal), instead of the 66 bytes previously allocated. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-15-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 274 +++++++++++++++++--------------- 1 file changed, 148 insertions(+), 126 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index d427a7b1ef64..c09d97051c7e 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -14,10 +14,12 @@ #include #include +#include #include #include -static int skip_atoi(const char **s) +static +int skip_atoi(const char **s) { int i = 0; @@ -32,16 +34,16 @@ static int skip_atoi(const char **s) * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. */ static -void put_dec_full4(char *buf, unsigned int r) +void put_dec_full4(char *end, unsigned int r) { int i; for (i = 0; i < 3; i++) { unsigned int q = (r * 0xccd) >> 15; - *buf++ = '0' + (r - q * 10); + *--end = '0' + (r - q * 10); r = q; } - *buf++ = '0' + r; + *--end = '0' + r; } /* put_dec is copied from lib/vsprintf.c with small modifications */ @@ -54,11 +56,11 @@ void put_dec_full4(char *buf, unsigned int r) * (second call in the put_dec code, assuming n is all-ones). */ static -unsigned int put_dec_helper4(char *buf, unsigned int x) +unsigned int put_dec_helper4(char *end, unsigned int x) { unsigned int q = (x * 0x346DC5D7ULL) >> 43; - put_dec_full4(buf, x - q * 10000); + put_dec_full4(end, x - q * 10000); return q; } @@ -68,10 +70,10 @@ unsigned int put_dec_helper4(char *buf, unsigned int x) * Performs no 64-bit division and hence should be fast on 32-bit machines. */ static -int put_dec(char *buf, unsigned long long n) +char *put_dec(char *end, unsigned long long n) { unsigned int d3, d2, d1, q, h; - char *p = buf; + char *p = end; d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */ h = (n >> 32); @@ -82,28 +84,59 @@ int put_dec(char *buf, unsigned long long n) = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */ q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff); q = put_dec_helper4(p, q); - p += 4; + p -= 4; q += 7671 * d3 + 9496 * d2 + 6 * d1; q = put_dec_helper4(p, q); - p += 4; + p -= 4; q += 4749 * d3 + 42 * d2; q = put_dec_helper4(p, q); - p += 4; + p -= 4; q += 281 * d3; q = put_dec_helper4(p, q); - p += 4; + p -= 4; put_dec_full4(p, q); - p += 4; + p -= 4; /* strip off the extra 0's we printed */ - while (p > buf && p[-1] == '0') - --p; + while (p < end && *p == '0') + ++p; - return p - buf; + return p; +} + +static +char *number(char *end, unsigned long long num, int base, char locase) +{ + /* + * locase = 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters + */ + + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + + switch (base) { + case 10: + if (num != 0) + end = put_dec(end, num); + break; + case 8: + for (; num != 0; num >>= 3) + *--end = '0' + (num & 07); + break; + case 16: + for (; num != 0; num >>= 4) + *--end = digits[num & 0xf] | locase; + break; + default: + unreachable(); + }; + + return end; } #define ZEROPAD 1 /* pad with zero */ @@ -114,95 +147,6 @@ int put_dec(char *buf, unsigned long long n) #define SMALL 32 /* Must be 32 == 0x20 */ #define SPECIAL 64 /* 0x */ -static char *number(char *str, long long num, int base, int size, int precision, - int type) -{ - /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ - static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ - - char tmp[66]; - char c, sign, locase; - int i; - - /* locase = 0 or 0x20. ORing digits or letters with 'locase' - * produces same digits or (maybe lowercased) letters */ - locase = (type & SMALL); - if (type & LEFT) - type &= ~ZEROPAD; - c = (type & ZEROPAD) ? '0' : ' '; - sign = 0; - if (type & SIGN) { - if (num < 0) { - sign = '-'; - num = -num; - size--; - } else if (type & PLUS) { - sign = '+'; - size--; - } else if (type & SPACE) { - sign = ' '; - size--; - } - } - if (type & SPECIAL) { - if (base == 16) - size -= 2; - else if (base == 8) - size--; - } - i = 0; - if (num == 0) - tmp[i++] = '0'; - else { - switch (base) { - case 10: - i += put_dec(&tmp[i], num); - break; - case 8: - while (num != 0) { - tmp[i++] = '0' + (num & 07); - num = (unsigned long long)num >> 3; - } - break; - case 16: - while (num != 0) { - tmp[i++] = digits[num & 0xf] | locase; - num = (unsigned long long)num >> 4; - } - break; - default: - unreachable(); - } - } - - if (i > precision) - precision = i; - size -= precision; - if (!(type & (ZEROPAD + LEFT))) - while (size-- > 0) - *str++ = ' '; - if (sign) - *str++ = sign; - if (type & SPECIAL) { - if (base == 8) { - *str++ = '0'; - } else if (base == 16) { - *str++ = '0'; - *str++ = ('X' | locase); - } - } - if (!(type & LEFT)) - while (size-- > 0) - *str++ = c; - while (i < precision--) - *str++ = '0'; - while (i-- > 0) - *str++ = tmp[i]; - while (size-- > 0) - *str++ = ' '; - return str; -} - static int get_flags(const char **fmt) { @@ -277,13 +221,33 @@ unsigned long long get_number(int sign, int qualifier, va_list *ap) } } +static +char get_sign(long long *num, int flags) +{ + if (!(flags & SIGN)) + return 0; + if (*num < 0) { + *num = -(*num); + return '-'; + } + if (flags & PLUS) + return '+'; + if (flags & SPACE) + return ' '; + return 0; +} + int vsprintf(char *buf, const char *fmt, va_list ap) { - int len; - unsigned long long num; - int i, base; + /* The maximum space required is to print a 64-bit number in octal */ + char tmp[(sizeof(unsigned long long) * 8 + 2) / 3]; + char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; + long long num; + int base; char *str; const char *s; + int len; + char sign; int flags; /* flags to number() */ @@ -326,6 +290,9 @@ int vsprintf(char *buf, const char *fmt, va_list ap) flags |= LEFT; } + if (flags & LEFT) + flags &= ~ZEROPAD; + /* get the precision */ precision = -1; if (*fmt == '.') { @@ -346,32 +313,25 @@ int vsprintf(char *buf, const char *fmt, va_list ap) } } + sign = 0; + switch (*fmt) { case 'c': - if (!(flags & LEFT)) - while (--field_width > 0) - *str++ = ' '; - *str++ = (unsigned char)va_arg(args, int); - while (--field_width > 0) - *str++ = ' '; - continue; + flags &= LEFT; + tmp[0] = (unsigned char)va_arg(args, int); + s = tmp; + precision = len = 1; + goto output; case 's': + flags &= LEFT; if (precision < 0) precision = INT_MAX; s = va_arg(args, char *); if (!s) s = precision < 6 ? "" : "(null)"; - len = strnlen(s, precision); - - if (!(flags & LEFT)) - while (len < field_width--) - *str++ = ' '; - for (i = 0; i < len; ++i) - *str++ = *s++; - while (len < field_width--) - *str++ = ' '; - continue; + precision = len = strnlen(s, precision); + goto output; /* integer number formats - set up the flags and "break" */ case 'o': @@ -394,6 +354,7 @@ int vsprintf(char *buf, const char *fmt, va_list ap) flags |= SIGN; fallthrough; case 'u': + flags &= ~SPECIAL; base = 10; break; @@ -410,7 +371,68 @@ int vsprintf(char *buf, const char *fmt, va_list ap) } else { num = get_number(flags & SIGN, qualifier, &args); } - str = number(str, num, base, field_width, precision, flags); + + sign = get_sign(&num, flags); + if (sign) + --field_width; + + s = number(tmp_end, num, base, flags & SMALL); + len = tmp_end - s; + /* default precision is 1 */ + if (precision < 0) + precision = 1; + /* precision is minimum number of digits to print */ + if (precision < len) + precision = len; + if (flags & SPECIAL) { + /* + * For octal, a leading 0 is printed only if necessary, + * i.e. if it's not already there because of the + * precision. + */ + if (base == 8 && precision == len) + ++precision; + /* + * For hexadecimal, the leading 0x is skipped if the + * output is empty, i.e. both the number and the + * precision are 0. + */ + if (base == 16 && precision > 0) + field_width -= 2; + else + flags &= ~SPECIAL; + } + /* + * For zero padding, increase the precision to fill the field + * width. + */ + if ((flags & ZEROPAD) && field_width > precision) + precision = field_width; + +output: + /* Calculate the padding necessary */ + field_width -= precision; + /* Leading padding with ' ' */ + if (!(flags & LEFT)) + while (field_width-- > 0) + *str++ = ' '; + /* sign */ + if (sign) + *str++ = sign; + /* 0x/0X for hexadecimal */ + if (flags & SPECIAL) { + *str++ = '0'; + *str++ = 'X' | (flags & SMALL); + } + /* Zero padding and excess precision */ + while (precision-- > len) + *str++ = '0'; + /* Actual output */ + while (len-- > 0) + *str++ = *s++; + /* Trailing padding with ' ' */ + while (field_width-- > 0) + *str++ = ' '; } *str = '\0'; -- cgit v1.2.3-70-g09d2 From f97ca2c816748e3b7dee58775632f9e9269071e6 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:07 -0400 Subject: efi/printf: Abort on invalid format If we get an invalid conversion specifier, bail out instead of trying to fix it up. The format string likely has a typo or assumed we support something that we don't, in either case the remaining arguments won't match up with the remaining format string. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-16-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index c09d97051c7e..cca6b802b028 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -359,12 +359,13 @@ int vsprintf(char *buf, const char *fmt, va_list ap) break; default: - *str++ = '%'; - if (*fmt) - *str++ = *fmt; - else - --fmt; - continue; + /* + * Bail out if the conversion specifier is invalid. + * There's probably a typo in the format string and the + * remaining specifiers are unlikely to match up with + * the arguments. + */ + goto fail; } if (*fmt == 'p') { num = (unsigned long)va_arg(args, void *); @@ -434,6 +435,7 @@ output: while (field_width-- > 0) *str++ = ' '; } +fail: *str = '\0'; va_end(args); -- cgit v1.2.3-70-g09d2 From 8fb331e10b63888e944a8a0dcf79b17e93b475ba Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:08 -0400 Subject: efi/printf: Turn vsprintf into vsnprintf Implement vsnprintf instead of vsprintf to avoid the possibility of a buffer overflow. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-17-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +++- drivers/firmware/efi/libstub/vsprintf.c | 42 +++++++++++++++----------- 2 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 56b3b84fd3bd..5ecafc57619a 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -60,10 +60,14 @@ int efi_printk(const char *fmt, ...) int printed; va_start(args, fmt); - printed = vsprintf(printf_buf, fmt, args); + printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); va_end(args); efi_puts(printf_buf); + if (printed >= sizeof(printf_buf)) { + efi_puts("[Message truncated]\n"); + return -1; + } return printed; } diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index cca6b802b028..a3265a81adca 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -17,6 +17,7 @@ #include #include #include +#include static int skip_atoi(const char **s) @@ -237,16 +238,22 @@ char get_sign(long long *num, int flags) return 0; } -int vsprintf(char *buf, const char *fmt, va_list ap) +#define PUTC(c) \ +do { \ + if (pos < size) \ + buf[pos] = (c); \ + ++pos; \ +} while (0); + +int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) { /* The maximum space required is to print a 64-bit number in octal */ char tmp[(sizeof(unsigned long long) * 8 + 2) / 3]; char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; long long num; int base; - char *str; const char *s; - int len; + size_t len, pos; char sign; int flags; /* flags to number() */ @@ -274,9 +281,9 @@ int vsprintf(char *buf, const char *fmt, va_list ap) */ va_copy(args, ap); - for (str = buf; *fmt; ++fmt) { + for (pos = 0; *fmt; ++fmt) { if (*fmt != '%' || *++fmt == '%') { - *str++ = *fmt; + PUTC(*fmt); continue; } @@ -416,40 +423,41 @@ output: /* Leading padding with ' ' */ if (!(flags & LEFT)) while (field_width-- > 0) - *str++ = ' '; + PUTC(' '); /* sign */ if (sign) - *str++ = sign; + PUTC(sign); /* 0x/0X for hexadecimal */ if (flags & SPECIAL) { - *str++ = '0'; - *str++ = 'X' | (flags & SMALL); + PUTC('0'); + PUTC( 'X' | (flags & SMALL)); } /* Zero padding and excess precision */ while (precision-- > len) - *str++ = '0'; + PUTC('0'); /* Actual output */ while (len-- > 0) - *str++ = *s++; + PUTC(*s++); /* Trailing padding with ' ' */ while (field_width-- > 0) - *str++ = ' '; + PUTC(' '); } fail: - *str = '\0'; - va_end(args); - return str - buf; + if (size) + buf[min(pos, size-1)] = '\0'; + + return pos; } -int sprintf(char *buf, const char *fmt, ...) +int snprintf(char *buf, size_t size, const char *fmt, ...) { va_list args; int i; va_start(args, fmt); - i = vsprintf(buf, fmt, args); + i = vsnprintf(buf, size, fmt, args); va_end(args); return i; } -- cgit v1.2.3-70-g09d2 From 23d5b73fbfd787c4c63841912b079eac000a7414 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Wed, 20 May 2020 19:07:54 +0200 Subject: efi/libstub: Implement printk-style logging Use the efi_printk function in efi_info/efi_err, and add efi_debug. This allows formatted output at different log levels. Add the notion of a loglevel instead of just quiet/not-quiet, and parse the efi=debug kernel parameter in addition to quiet. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200520170223.GA3333632@rani.riverdale.lan/ Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 29 ++++++++++++++++++++++++-- drivers/firmware/efi/libstub/efistub.h | 14 +++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 5ecafc57619a..1f5a00b4f201 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -11,6 +11,7 @@ #include #include +#include /* For CONSOLE_LOGLEVEL_* */ #include #include "efistub.h" @@ -18,7 +19,7 @@ bool efi_nochunk; bool efi_nokaslr; bool efi_noinitrd; -bool efi_quiet; +int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; bool efi_novamap; static bool efi_nosoftreserve; @@ -58,6 +59,28 @@ int efi_printk(const char *fmt, ...) char printf_buf[256]; va_list args; int printed; + int loglevel = printk_get_level(fmt); + + switch (loglevel) { + case '0' ... '9': + loglevel -= '0'; + break; + default: + /* + * Use loglevel -1 for cases where we just want to print to + * the screen. + */ + loglevel = -1; + break; + } + + if (loglevel >= efi_loglevel) + return 0; + + if (loglevel >= 0) + efi_puts("EFI stub: "); + + fmt = printk_skip_level(fmt); va_start(args, fmt); printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); @@ -100,7 +123,7 @@ efi_status_t efi_parse_options(char const *cmdline) if (!strcmp(param, "nokaslr")) { efi_nokaslr = true; } else if (!strcmp(param, "quiet")) { - efi_quiet = true; + efi_loglevel = CONSOLE_LOGLEVEL_QUIET; } else if (!strcmp(param, "noinitrd")) { efi_noinitrd = true; } else if (!strcmp(param, "efi") && val) { @@ -114,6 +137,8 @@ efi_status_t efi_parse_options(char const *cmdline) efi_disable_pci_dma = true; if (parse_option_str(val, "no_disable_early_pci_dma")) efi_disable_pci_dma = false; + if (parse_option_str(val, "debug")) + efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; } else if (!strcmp(param, "video") && val && strstarts(val, "efifb:")) { efi_parse_option_graphics(val + strlen("efifb:")); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index caa7dcc71c69..3a323a009836 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,7 @@ extern bool efi_nochunk; extern bool efi_nokaslr; extern bool efi_noinitrd; -extern bool efi_quiet; +extern int efi_loglevel; extern bool efi_novamap; extern const efi_system_table_t *efi_system_table; @@ -49,11 +50,12 @@ extern const efi_system_table_t *efi_system_table; #endif -#define efi_info(msg) do { \ - if (!efi_quiet) efi_puts("EFI stub: "msg); \ -} while (0) - -#define efi_err(msg) efi_puts("EFI stub: ERROR: "msg) +#define efi_info(fmt, ...) \ + efi_printk(KERN_INFO fmt, ##__VA_ARGS__) +#define efi_err(fmt, ...) \ + efi_printk(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__) +#define efi_debug(fmt, ...) \ + efi_printk(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__) /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var -- cgit v1.2.3-70-g09d2 From 9b47c5275614a16fd64359fab73fe6c736bf57a0 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:10 -0400 Subject: efi/libstub: Add definitions for console input and events Add the required typedefs etc for using con_in's simple text input protocol, and for using the boottime event services. Also add the prototype for the "stall" boot service. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-19-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 10 +++++ arch/x86/xen/efi.c | 2 +- drivers/firmware/efi/libstub/efistub.h | 77 +++++++++++++++++++++++++++++++--- include/linux/efi.h | 3 +- 4 files changed, 85 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 6b9ab0d8b2a7..89dcc7aa7e2c 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -9,6 +9,7 @@ #include #include #include +#include extern unsigned long efi_fw_vendor, efi_config_table; @@ -293,6 +294,15 @@ static inline u32 efi64_convert_status(efi_status_t status) #define __efi64_argmap_allocate_pool(type, size, buffer) \ ((type), (size), efi64_zero_upper(buffer)) +#define __efi64_argmap_create_event(type, tpl, f, c, event) \ + ((type), (tpl), (f), (c), efi64_zero_upper(event)) + +#define __efi64_argmap_set_timer(event, type, time) \ + ((event), (type), lower_32_bits(time), upper_32_bits(time)) + +#define __efi64_argmap_wait_for_event(num, event, index) \ + ((num), (event), efi64_zero_upper(index)) + #define __efi64_argmap_handle_protocol(handle, protocol, interface) \ ((handle), (protocol), efi64_zero_upper(interface)) diff --git a/arch/x86/xen/efi.c b/arch/x86/xen/efi.c index 1abe455d926a..205a9bc981b0 100644 --- a/arch/x86/xen/efi.c +++ b/arch/x86/xen/efi.c @@ -29,7 +29,7 @@ static efi_system_table_t efi_systab_xen __initdata = { .fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */ .fw_revision = 0, /* Initialized later. */ .con_in_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ - .con_in = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ + .con_in = NULL, /* Not used under Xen. */ .con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ .con_out = NULL, /* Not used under Xen. */ .stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 3a323a009836..c7c03099367f 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -111,6 +111,16 @@ void efi_set_u64_split(u64 data, u32 *lo, u32 *hi) #define EFI_LOCATE_BY_REGISTER_NOTIFY 1 #define EFI_LOCATE_BY_PROTOCOL 2 +/* + * boottime->stall takes the time period in microseconds + */ +#define EFI_USEC_PER_SEC 1000000 + +/* + * boottime->set_timer takes the time in 100ns units + */ +#define EFI_100NSEC_PER_USEC ((u64)10) + struct efi_boot_memmap { efi_memory_desc_t **map; unsigned long *map_size; @@ -122,6 +132,39 @@ struct efi_boot_memmap { typedef struct efi_generic_dev_path efi_device_path_protocol_t; +typedef void *efi_event_t; +/* Note that notifications won't work in mixed mode */ +typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *); + +#define EFI_EVT_TIMER 0x80000000U +#define EFI_EVT_RUNTIME 0x40000000U +#define EFI_EVT_NOTIFY_WAIT 0x00000100U +#define EFI_EVT_NOTIFY_SIGNAL 0x00000200U + +/* + * boottime->wait_for_event takes an array of events as input. + * Provide a helper to set it up correctly for mixed mode. + */ +static inline +void efi_set_event_at(efi_event_t *events, size_t idx, efi_event_t event) +{ + if (efi_is_native()) + events[idx] = event; + else + ((u32 *)events)[idx] = (u32)(unsigned long)event; +} + +#define EFI_TPL_APPLICATION 4 +#define EFI_TPL_CALLBACK 8 +#define EFI_TPL_NOTIFY 16 +#define EFI_TPL_HIGH_LEVEL 31 + +typedef enum { + EfiTimerCancel, + EfiTimerPeriodic, + EfiTimerRelative +} EFI_TIMER_DELAY; + /* * EFI Boot Services table */ @@ -140,11 +183,16 @@ union efi_boot_services { efi_status_t (__efiapi *allocate_pool)(int, unsigned long, void **); efi_status_t (__efiapi *free_pool)(void *); - void *create_event; - void *set_timer; - void *wait_for_event; + efi_status_t (__efiapi *create_event)(u32, unsigned long, + efi_event_notify_t, void *, + efi_event_t *); + efi_status_t (__efiapi *set_timer)(efi_event_t, + EFI_TIMER_DELAY, u64); + efi_status_t (__efiapi *wait_for_event)(unsigned long, + efi_event_t *, + unsigned long *); void *signal_event; - void *close_event; + efi_status_t (__efiapi *close_event)(efi_event_t); void *check_event; void *install_protocol_interface; void *reinstall_protocol_interface; @@ -171,7 +219,7 @@ union efi_boot_services { efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, unsigned long); void *get_next_monotonic_count; - void *stall; + efi_status_t (__efiapi *stall)(unsigned long); void *set_watchdog_timer; void *connect_controller; efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, @@ -256,6 +304,25 @@ union efi_uga_draw_protocol { } mixed_mode; }; +typedef struct { + u16 scan_code; + efi_char16_t unicode_char; +} efi_input_key_t; + +union efi_simple_text_input_protocol { + struct { + void *reset; + efi_status_t (__efiapi *read_keystroke)(efi_simple_text_input_protocol_t *, + efi_input_key_t *); + efi_event_t wait_for_key; + }; + struct { + u32 reset; + u32 read_keystroke; + u32 wait_for_key; + } mixed_mode; +}; + union efi_simple_text_output_protocol { struct { void *reset; diff --git a/include/linux/efi.h b/include/linux/efi.h index 9b7c7ec319ac..974648db0c68 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -426,6 +426,7 @@ typedef struct { u32 tables; } efi_system_table_32_t; +typedef union efi_simple_text_input_protocol efi_simple_text_input_protocol_t; typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t; typedef union { @@ -434,7 +435,7 @@ typedef union { unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */ u32 fw_revision; unsigned long con_in_handle; - unsigned long con_in; + efi_simple_text_input_protocol_t *con_in; unsigned long con_out_handle; efi_simple_text_output_protocol_t *con_out; unsigned long stderr_handle; -- cgit v1.2.3-70-g09d2 From 14c574f35cfbc9272fc67b41f074c847db139652 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:11 -0400 Subject: efi/gop: Add an option to list out the available GOP modes Add video=efifb:list option to list the modes that are available. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-20-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- Documentation/fb/efifb.rst | 5 ++ drivers/firmware/efi/libstub/efi-stub-helper.c | 35 ++++++++++ drivers/firmware/efi/libstub/efistub.h | 2 + drivers/firmware/efi/libstub/gop.c | 97 +++++++++++++++++++++++++- include/linux/efi.h | 1 + 5 files changed, 139 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/fb/efifb.rst b/Documentation/fb/efifb.rst index 519550517fd4..6badff64756f 100644 --- a/Documentation/fb/efifb.rst +++ b/Documentation/fb/efifb.rst @@ -63,4 +63,9 @@ auto with the highest resolution, it will choose one with the highest color depth. +list + The EFI stub will list out all the display modes that are available. A + specific mode can then be chosen using one of the above options for the + next boot. + Edgar Hucek diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 1f5a00b4f201..f338d149aaa5 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -463,3 +463,38 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image, return status; } + +efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) +{ + efi_event_t events[2], timer; + unsigned long index; + efi_simple_text_input_protocol_t *con_in; + efi_status_t status; + + con_in = efi_table_attr(efi_system_table, con_in); + if (!con_in) + return EFI_UNSUPPORTED; + efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key)); + + status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer); + if (status != EFI_SUCCESS) + return status; + + status = efi_bs_call(set_timer, timer, EfiTimerRelative, + EFI_100NSEC_PER_USEC * usec); + if (status != EFI_SUCCESS) + return status; + efi_set_event_at(events, 1, timer); + + status = efi_bs_call(wait_for_event, 2, events, &index); + if (status == EFI_SUCCESS) { + if (index == 0) + status = efi_call_proto(con_in, read_keystroke, key); + else + status = EFI_TIMEOUT; + } + + efi_bs_call(close_event, timer); + + return status; +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index c7c03099367f..ad7e0406d0ba 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -323,6 +323,8 @@ union efi_simple_text_input_protocol { } mixed_mode; }; +efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key); + union efi_simple_text_output_protocol { struct { void *reset; diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 34c0cba2c8bf..ea5da307d542 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -19,7 +19,8 @@ enum efi_cmdline_option { EFI_CMDLINE_NONE, EFI_CMDLINE_MODE_NUM, EFI_CMDLINE_RES, - EFI_CMDLINE_AUTO + EFI_CMDLINE_AUTO, + EFI_CMDLINE_LIST }; static struct { @@ -100,6 +101,19 @@ static bool parse_auto(char *option, char **next) return true; } +static bool parse_list(char *option, char **next) +{ + if (!strstarts(option, "list")) + return false; + option += strlen("list"); + if (*option && *option++ != ',') + return false; + cmdline.option = EFI_CMDLINE_LIST; + + *next = option; + return true; +} + void efi_parse_option_graphics(char *option) { while (*option) { @@ -109,6 +123,8 @@ void efi_parse_option_graphics(char *option) continue; if (parse_auto(option, &option)) continue; + if (parse_list(option, &option)) + continue; while (*option && *option++ != ',') ; @@ -290,6 +306,82 @@ static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop) return best_mode; } +static u32 choose_mode_list(efi_graphics_output_protocol_t *gop) +{ + efi_status_t status; + + efi_graphics_output_protocol_mode_t *mode; + efi_graphics_output_mode_info_t *info; + unsigned long info_size; + + u32 max_mode, cur_mode; + int pf; + efi_pixel_bitmask_t pi; + u32 m, w, h; + u8 d; + const char *dstr; + bool valid; + efi_input_key_t key; + + mode = efi_table_attr(gop, mode); + + cur_mode = efi_table_attr(mode, mode); + max_mode = efi_table_attr(mode, max_mode); + + efi_printk("Available graphics modes are 0-%u\n", max_mode-1); + efi_puts(" * = current mode\n" + " - = unusable mode\n"); + for (m = 0; m < max_mode; m++) { + status = efi_call_proto(gop, query_mode, m, + &info_size, &info); + if (status != EFI_SUCCESS) + continue; + + pf = info->pixel_format; + pi = info->pixel_information; + w = info->horizontal_resolution; + h = info->vertical_resolution; + + efi_bs_call(free_pool, info); + + valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX); + d = 0; + switch (pf) { + case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: + dstr = "rgb"; + break; + case PIXEL_BGR_RESERVED_8BIT_PER_COLOR: + dstr = "bgr"; + break; + case PIXEL_BIT_MASK: + dstr = ""; + d = pixel_bpp(pf, pi); + break; + case PIXEL_BLT_ONLY: + dstr = "blt"; + break; + default: + dstr = "xxx"; + break; + } + + efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n", + m, + m == cur_mode ? '*' : ' ', + !valid ? '-' : ' ', + w, h, dstr, d); + } + + efi_puts("\nPress any key to continue (or wait 10 seconds)\n"); + status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key); + if (status != EFI_SUCCESS && status != EFI_TIMEOUT) { + efi_err("Unable to read key, continuing in 10 seconds\n"); + efi_bs_call(stall, 10 * EFI_USEC_PER_SEC); + } + + return cur_mode; +} + static void set_mode(efi_graphics_output_protocol_t *gop) { efi_graphics_output_protocol_mode_t *mode; @@ -305,6 +397,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop) case EFI_CMDLINE_AUTO: new_mode = choose_mode_auto(gop); break; + case EFI_CMDLINE_LIST: + new_mode = choose_mode_list(gop); + break; default: return; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 974648db0c68..609201bd4682 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -39,6 +39,7 @@ #define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1))) #define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1))) #define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG-1))) +#define EFI_TIMEOUT (18 | (1UL << (BITS_PER_LONG-1))) #define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1))) #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1))) -- cgit v1.2.3-70-g09d2 From d850a2ff918be0691f8d0544a2156c856c42da5b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:12 -0400 Subject: efi/printf: Add support for wchar_t (UTF-16) Support %lc and %ls to output UTF-16 strings (converted to UTF-8). Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-21-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 111 ++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index a3265a81adca..e65ef49a54cd 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -147,6 +147,7 @@ char *number(char *end, unsigned long long num, int base, char locase) #define LEFT 16 /* left justified */ #define SMALL 32 /* Must be 32 == 0x20 */ #define SPECIAL 64 /* 0x */ +#define WIDE 128 /* UTF-16 string */ static int get_flags(const char **fmt) @@ -238,6 +239,58 @@ char get_sign(long long *num, int flags) return 0; } +static +size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen) +{ + size_t len, clen; + + for (len = 0; len < maxlen && *s16; len += clen) { + u16 c0 = *s16++; + + /* First, get the length for a BMP character */ + clen = 1 + (c0 >= 0x80) + (c0 >= 0x800); + if (len + clen > maxlen) + break; + /* + * If this is a high surrogate, and we're already at maxlen, we + * can't include the character if it's a valid surrogate pair. + * Avoid accessing one extra word just to check if it's valid + * or not. + */ + if ((c0 & 0xfc00) == 0xd800) { + if (len + clen == maxlen) + break; + if ((*s16 & 0xfc00) == 0xdc00) { + ++s16; + ++clen; + } + } + } + + return len; +} + +static +u32 utf16_to_utf32(const u16 **s16) +{ + u16 c0, c1; + + c0 = *(*s16)++; + /* not a surrogate */ + if ((c0 & 0xf800) != 0xd800) + return c0; + /* invalid: low surrogate instead of high */ + if (c0 & 0x0400) + return 0xfffd; + c1 = **s16; + /* invalid: missing low surrogate */ + if ((c1 & 0xfc00) != 0xdc00) + return 0xfffd; + /* valid surrogate pair */ + ++(*s16); + return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1; +} + #define PUTC(c) \ do { \ if (pos < size) \ @@ -325,18 +378,31 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) switch (*fmt) { case 'c': flags &= LEFT; - tmp[0] = (unsigned char)va_arg(args, int); s = tmp; - precision = len = 1; + if (qualifier == 'l') { + ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int); + ((u16 *)tmp)[1] = L'\0'; + precision = INT_MAX; + goto wstring; + } else { + tmp[0] = (unsigned char)va_arg(args, int); + precision = len = 1; + } goto output; case 's': flags &= LEFT; if (precision < 0) precision = INT_MAX; - s = va_arg(args, char *); + s = va_arg(args, void *); if (!s) s = precision < 6 ? "" : "(null)"; + else if (qualifier == 'l') { + wstring: + flags |= WIDE; + precision = len = utf16s_utf8nlen((const u16 *)s, precision); + goto output; + } precision = len = strnlen(s, precision); goto output; @@ -436,8 +502,43 @@ output: while (precision-- > len) PUTC('0'); /* Actual output */ - while (len-- > 0) - PUTC(*s++); + if (flags & WIDE) { + const u16 *ws = (const u16 *)s; + + while (len-- > 0) { + u32 c32 = utf16_to_utf32(&ws); + u8 *s8; + size_t clen; + + if (c32 < 0x80) { + PUTC(c32); + continue; + } + + /* Number of trailing octets */ + clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000); + + len -= clen; + s8 = (u8 *)&buf[pos]; + + /* Avoid writing partial character */ + PUTC('\0'); + pos += clen; + if (pos >= size) + continue; + + /* Set high bits of leading octet */ + *s8 = (0xf00 >> 1) >> clen; + /* Write trailing octets in reverse order */ + for (s8 += clen; clen; --clen, c32 >>= 6) + *s8-- = 0x80 | (c32 & 0x3f); + /* Set low bits of leading octet */ + *s8 |= c32; + } + } else { + while (len-- > 0) + PUTC(*s++); + } /* Trailing padding with ' ' */ while (field_width-- > 0) PUTC(' '); -- cgit v1.2.3-70-g09d2 From 4b75bd363de3f3429c62359be4e4aa4da7fa233b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:13 -0400 Subject: efi/libstub: Add UTF-8 decoding to efi_puts In order to be able to use the UTF-16 support added to vsprintf in the previous commit, enhance efi_puts to decode UTF-8 into UTF-16. Invalid UTF-8 encodings are passed through unchanged. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-22-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 67 ++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index f338d149aaa5..0d0007355c1e 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -36,17 +36,74 @@ void efi_char16_puts(efi_char16_t *str) output_string, str); } +static +u32 utf8_to_utf32(const u8 **s8) +{ + u32 c32; + u8 c0, cx; + size_t clen, i; + + c0 = cx = *(*s8)++; + /* + * The position of the most-significant 0 bit gives us the length of + * a multi-octet encoding. + */ + for (clen = 0; cx & 0x80; ++clen) + cx <<= 1; + /* + * If the 0 bit is in position 8, this is a valid single-octet + * encoding. If the 0 bit is in position 7 or positions 1-3, the + * encoding is invalid. + * In either case, we just return the first octet. + */ + if (clen < 2 || clen > 4) + return c0; + /* Get the bits from the first octet. */ + c32 = cx >> clen--; + for (i = 0; i < clen; ++i) { + /* Trailing octets must have 10 in most significant bits. */ + cx = (*s8)[i] ^ 0x80; + if (cx & 0xc0) + return c0; + c32 = (c32 << 6) | cx; + } + /* + * Check for validity: + * - The character must be in the Unicode range. + * - It must not be a surrogate. + * - It must be encoded using the correct number of octets. + */ + if (c32 > 0x10ffff || + (c32 & 0xf800) == 0xd800 || + clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) + return c0; + *s8 += clen; + return c32; +} + void efi_puts(const char *str) { efi_char16_t buf[128]; size_t pos = 0, lim = ARRAY_SIZE(buf); + const u8 *s8 = (const u8 *)str; + u32 c32; - while (*str) { - if (*str == '\n') + while (*s8) { + if (*s8 == '\n') buf[pos++] = L'\r'; - /* Cast to unsigned char to avoid sign-extension */ - buf[pos++] = (unsigned char)(*str++); - if (*str == '\0' || pos >= lim - 2) { + c32 = utf8_to_utf32(&s8); + if (c32 < 0x10000) { + /* Characters in plane 0 use a single word. */ + buf[pos++] = c32; + } else { + /* + * Characters in other planes encode into a surrogate + * pair. + */ + buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); + buf[pos++] = 0xdc00 + (c32 & 0x3ff); + } + if (*s8 == '\0' || pos >= lim - 2) { buf[pos] = L'\0'; efi_char16_puts(buf); pos = 0; -- cgit v1.2.3-70-g09d2 From a713979e443d4036e76e13bb3d30e9fa248915aa Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:14 -0400 Subject: efi/libstub: Use %ls for filename efi_printk can now handle the UTF-16 filename, so print it using efi_err instead of a separate efi_char16_puts call. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-23-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/file.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 933536c5236a..2005e33b33d5 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -46,9 +46,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { - efi_err("Failed to open file: "); - efi_char16_puts(fi->filename); - efi_puts("\n"); + efi_err("Failed to open file: %ls\n", fi->filename); return status; } -- cgit v1.2.3-70-g09d2 From 15c316bcbc23b777eae4e21f129526190aa20af1 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:15 -0400 Subject: efi/libstub: Get the exact UTF-8 length efi_convert_cmdline currently overestimates the length of the equivalent UTF-8 encoding. snprintf can now be used to do the conversion to UTF-8, however, it does not have a way to specify the size of the UTF-16 string, only the size of the resulting UTF-8 string. So in order to use it, we need to precalculate the exact UTF-8 size. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-24-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 44 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0d0007355c1e..4d544f395403 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -205,15 +205,6 @@ efi_status_t efi_parse_options(char const *cmdline) return EFI_SUCCESS; } -/* - * Get the number of UTF-8 bytes corresponding to an UTF-16 character. - * This overestimates for surrogates, but that is okay. - */ -static int efi_utf8_bytes(u16 c) -{ - return 1 + (c >= 0x80) + (c >= 0x800); -} - /* * Convert an UTF-16 string, not necessarily null terminated, to UTF-8. */ @@ -274,10 +265,39 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, if (options) { s2 = options; - while (*s2 && *s2 != '\n' - && options_chars < load_options_chars) { - options_bytes += efi_utf8_bytes(*s2++); + while (options_chars < load_options_chars) { + u16 c = *s2++; + + if (c == L'\0' || c == L'\n') + break; + /* + * Get the number of UTF-8 bytes corresponding to a + * UTF-16 character. + * The first part handles everything in the BMP. + */ + options_bytes += 1 + (c >= 0x80) + (c >= 0x800); options_chars++; + /* + * Add one more byte for valid surrogate pairs. Invalid + * surrogates will be replaced with 0xfffd and take up + * only 3 bytes. + */ + if ((c & 0xfc00) == 0xd800) { + /* + * If the very last word is a high surrogate, + * we must ignore it since we can't access the + * low surrogate. + */ + if (options_chars == load_options_chars) { + options_bytes -= 3; + options_chars--; + break; + } else if ((*s2 & 0xfc00) == 0xdc00) { + options_bytes++; + options_chars++; + s2++; + } + } } } -- cgit v1.2.3-70-g09d2 From 04b24409b42a9fcae2379a6ddcf9c827b9af7a45 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:16 -0400 Subject: efi/libstub: Use snprintf with %ls to convert the command line Now we can use snprintf to do the UTF-16 to UTF-8 translation for the command line. Drop the special "zero" trick to handle an empty command line. This was unnecessary even before this since with options_chars == 0, efi_utf16_to_utf8 would not have accessed options at all. snprintf won't access it either with a precision of 0. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200518190716.751506-25-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 66 +++----------------------- 1 file changed, 6 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 4d544f395403..9fb107e9355b 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -205,46 +205,6 @@ efi_status_t efi_parse_options(char const *cmdline) return EFI_SUCCESS; } -/* - * Convert an UTF-16 string, not necessarily null terminated, to UTF-8. - */ -static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n) -{ - unsigned int c; - - while (n--) { - c = *src++; - if (n && c >= 0xd800 && c <= 0xdbff && - *src >= 0xdc00 && *src <= 0xdfff) { - c = 0x10000 + ((c & 0x3ff) << 10) + (*src & 0x3ff); - src++; - n--; - } - if (c >= 0xd800 && c <= 0xdfff) - c = 0xfffd; /* Unmatched surrogate */ - if (c < 0x80) { - *dst++ = c; - continue; - } - if (c < 0x800) { - *dst++ = 0xc0 + (c >> 6); - goto t1; - } - if (c < 0x10000) { - *dst++ = 0xe0 + (c >> 12); - goto t2; - } - *dst++ = 0xf0 + (c >> 18); - *dst++ = 0x80 + ((c >> 12) & 0x3f); - t2: - *dst++ = 0x80 + ((c >> 6) & 0x3f); - t1: - *dst++ = 0x80 + (c & 0x3f); - } - - return dst; -} - /* * Convert the unicode UEFI command line to ASCII to pass to kernel. * Size of memory allocated return in *cmd_line_len. @@ -254,18 +214,15 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, unsigned long max_addr) { const u16 *s2; - u8 *s1 = NULL; unsigned long cmdline_addr = 0; - int load_options_chars = efi_table_attr(image, load_options_size) / 2; + int options_chars = efi_table_attr(image, load_options_size) / 2; const u16 *options = efi_table_attr(image, load_options); int options_bytes = 0; /* UTF-8 bytes */ - int options_chars = 0; /* UTF-16 chars */ efi_status_t status; - u16 zero = 0; if (options) { s2 = options; - while (options_chars < load_options_chars) { + while (options_chars--) { u16 c = *s2++; if (c == L'\0' || c == L'\n') @@ -276,7 +233,6 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, * The first part handles everything in the BMP. */ options_bytes += 1 + (c >= 0x80) + (c >= 0x800); - options_chars++; /* * Add one more byte for valid surrogate pairs. Invalid * surrogates will be replaced with 0xfffd and take up @@ -288,35 +244,25 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, * we must ignore it since we can't access the * low surrogate. */ - if (options_chars == load_options_chars) { + if (!options_chars) { options_bytes -= 3; - options_chars--; - break; } else if ((*s2 & 0xfc00) == 0xdc00) { options_bytes++; - options_chars++; + options_chars--; s2++; } } } } - if (!options_chars) { - /* No command line options, so return empty string*/ - options = &zero; - } - options_bytes++; /* NUL termination */ status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr); if (status != EFI_SUCCESS) return NULL; - s1 = (u8 *)cmdline_addr; - s2 = (const u16 *)options; - - s1 = efi_utf16_to_utf8(s1, s2, options_chars); - *s1 = '\0'; + snprintf((char *)cmdline_addr, options_bytes, "%.*ls", + options_bytes - 1, options); *cmd_line_len = options_bytes; return (char *)cmdline_addr; -- cgit v1.2.3-70-g09d2 From 80b1bfe1cb2f20225fb2d8f11d44af4a9d765396 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Wed, 20 May 2020 20:29:21 -0400 Subject: efi/libstub: Don't parse overlong command lines Check if the command line passed in is larger than COMMAND_LINE_SIZE, and truncate it to the last full argument if so. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200521002921.69650-1-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 28 +++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 9fb107e9355b..dbfaf8dcdda2 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -9,10 +9,12 @@ #include +#include #include #include #include /* For CONSOLE_LOGLEVEL_* */ #include +#include #include "efistub.h" @@ -217,22 +219,33 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, unsigned long cmdline_addr = 0; int options_chars = efi_table_attr(image, load_options_size) / 2; const u16 *options = efi_table_attr(image, load_options); - int options_bytes = 0; /* UTF-8 bytes */ + int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ + bool in_quote = false; efi_status_t status; if (options) { s2 = options; - while (options_chars--) { + while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { u16 c = *s2++; - if (c == L'\0' || c == L'\n') - break; + if (c < 0x80) { + if (c == L'\0' || c == L'\n') + break; + if (c == L'"') + in_quote = !in_quote; + else if (!in_quote && isspace((char)c)) + safe_options_bytes = options_bytes; + + options_bytes++; + continue; + } + /* * Get the number of UTF-8 bytes corresponding to a * UTF-16 character. * The first part handles everything in the BMP. */ - options_bytes += 1 + (c >= 0x80) + (c >= 0x800); + options_bytes += 2 + (c >= 0x800); /* * Add one more byte for valid surrogate pairs. Invalid * surrogates will be replaced with 0xfffd and take up @@ -253,6 +266,11 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, } } } + if (options_bytes >= COMMAND_LINE_SIZE) { + options_bytes = safe_options_bytes; + efi_err("Command line is too long: truncated to %d bytes\n", + options_bytes); + } } options_bytes++; /* NUL termination */ -- cgit v1.2.3-70-g09d2 From 27cd5511feffea3956d081585316272795e45bce Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 19 May 2020 10:43:01 +0200 Subject: efi/libstub: Use pool allocation for the command line Now that we removed the memory limit for the allocation of the command line, there is no longer a need to use the page based allocator so switch to a pool allocation instead. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +++--- drivers/firmware/efi/libstub/efi-stub.c | 4 ++-- drivers/firmware/efi/libstub/efistub.h | 3 +-- drivers/firmware/efi/libstub/x86-stub.c | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index dbfaf8dcdda2..89f075275300 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -212,8 +212,7 @@ efi_status_t efi_parse_options(char const *cmdline) * Size of memory allocated return in *cmd_line_len. * Returns NULL on error. */ -char *efi_convert_cmdline(efi_loaded_image_t *image, - int *cmd_line_len, unsigned long max_addr) +char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) { const u16 *s2; unsigned long cmdline_addr = 0; @@ -275,7 +274,8 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, options_bytes++; /* NUL termination */ - status = efi_allocate_pages(options_bytes, &cmdline_addr, max_addr); + status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, + (void **)&cmdline_addr); if (status != EFI_SUCCESS) return NULL; diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index c2484bf75c5d..cc8d6f510a89 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -198,7 +198,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) * protocol. We are going to copy the command line into the * device tree, so this can be allocated anywhere. */ - cmdline_ptr = efi_convert_cmdline(image, &cmdline_size, ULONG_MAX); + cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); if (!cmdline_ptr) { efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); status = EFI_OUT_OF_RESOURCES; @@ -339,7 +339,7 @@ fail_free_image: fail_free_screeninfo: free_screen_info(si); fail_free_cmdline: - efi_free(cmdline_size, (unsigned long)cmdline_ptr); + efi_bs_call(free_pool, cmdline_ptr); fail: return status; } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index ad7e0406d0ba..1de8dc02529a 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -708,8 +708,7 @@ __printf(1, 2) int efi_printk(char const *fmt, ...); void efi_free(unsigned long size, unsigned long addr); -char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len, - unsigned long max_addr); +char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 37154bb93c59..072b7cf40475 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -404,7 +404,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, hdr->type_of_loader = 0x21; /* Convert unicode cmdline to ascii */ - cmdline_ptr = efi_convert_cmdline(image, &options_size, ULONG_MAX); + cmdline_ptr = efi_convert_cmdline(image, &options_size); if (!cmdline_ptr) goto fail; -- cgit v1.2.3-70-g09d2 From d8bd8c6e2cfab8b78b537715255be8d7557791c0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 22 May 2020 18:15:49 +0200 Subject: efi/efivars: Add missing kobject_put() in sysfs entry creation error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation provided by kobject_init_and_add() clearly spells out the need to call kobject_put() on the kobject if an error is returned. Add this missing call to the error path. Cc: Reported-by: 亿一 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efivars.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 78ad1ba8c987..26528a46d99e 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -522,8 +522,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, NULL, "%s", short_name); kfree(short_name); - if (ret) + if (ret) { + kobject_put(&new_var->kobj); return ret; + } kobject_uevent(&new_var->kobj, KOBJ_ADD); if (efivar_entry_add(new_var, &efivar_sysfs_list)) { -- cgit v1.2.3-70-g09d2 From 6e99d3213b10ee18428d6d20715ad6c0e89ead4d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 23 May 2020 11:01:57 +0200 Subject: efi/libstub: Add missing prototype for PE/COFF entry point Fix a missing prototype warning by adding a forward declaration for the PE/COFF entrypoint, and while at it, align the function name between the x86 and ARM versions of the stub. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/efi-header.S | 2 +- arch/arm64/kernel/efi-entry.S | 2 +- arch/arm64/kernel/efi-header.S | 2 +- drivers/firmware/efi/libstub/efi-stub.c | 3 ++- drivers/firmware/efi/libstub/efistub.h | 3 +++ 5 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S index 62286da318e7..c0e7a745103e 100644 --- a/arch/arm/boot/compressed/efi-header.S +++ b/arch/arm/boot/compressed/efi-header.S @@ -60,7 +60,7 @@ optional_header: .long __pecoff_code_size @ SizeOfCode .long __pecoff_data_size @ SizeOfInitializedData .long 0 @ SizeOfUninitializedData - .long efi_entry - start @ AddressOfEntryPoint + .long efi_pe_entry - start @ AddressOfEntryPoint .long start_offset @ BaseOfCode .long __pecoff_data_start - start @ BaseOfData diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S index 1a03618df0df..5664af1e9260 100644 --- a/arch/arm64/kernel/efi-entry.S +++ b/arch/arm64/kernel/efi-entry.S @@ -14,7 +14,7 @@ SYM_CODE_START(efi_enter_kernel) /* - * efi_entry() will have copied the kernel image if necessary and we + * efi_pe_entry() will have copied the kernel image if necessary and we * end up here with device tree address in x1 and the kernel entry * point stored in x0. Save those values in registers which are * callee preserved. diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S index 914999ccaf8a..f2457d4b76a4 100644 --- a/arch/arm64/kernel/efi-header.S +++ b/arch/arm64/kernel/efi-header.S @@ -27,7 +27,7 @@ optional_header: .long __initdata_begin - efi_header_end // SizeOfCode .long __pecoff_data_size // SizeOfInitializedData .long 0 // SizeOfUninitializedData - .long __efistub_efi_entry - _head // AddressOfEntryPoint + .long __efistub_efi_pe_entry - _head // AddressOfEntryPoint .long efi_header_end - _head // BaseOfCode extra_header_fields: diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index cc8d6f510a89..9b7634369158 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -140,7 +140,8 @@ asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, * for both archictectures, with the arch-specific code provided in the * handle_kernel_image() function. */ -efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) +efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, + efi_system_table_t *sys_table_arg) { efi_loaded_image_t *image; efi_status_t status; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 1de8dc02529a..03f74608b963 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -40,6 +40,9 @@ extern bool efi_novamap; extern const efi_system_table_t *efi_system_table; +efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, + efi_system_table_t *sys_table_arg); + #ifndef ARCH_HAS_EFISTUB_WRAPPERS #define efi_is_native() (true) -- cgit v1.2.3-70-g09d2 From e9524fb97ab5b41b85e1d3408f8e513433798f3c Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Wed, 27 May 2020 19:26:02 -0400 Subject: efi/x86: Don't blow away existing initrd Commit 987053a30016 ("efi/x86: Move command-line initrd loading to efi_main") moved the command-line initrd loading into efi_main(), with a check to ensure that it was attempted only if the EFI stub was booted via efi_pe_entry rather than the EFI handover entry. However, in the case where it was booted via handover entry, and thus an initrd may have already been loaded by the bootloader, it then wrote 0 for the initrd address and size, removing any existing initrd. Fix this by checking if size is positive before setting the fields in the bootparams structure. Fixes: 987053a30016 ("efi/x86: Move command-line initrd loading to efi_main") Reported-by: Dan Williams Signed-off-by: Arvind Sankar Signed-off-by: Borislav Petkov Reviewed-by: Ard Biesheuvel Tested-by: Dan Williams Link: https://lkml.kernel.org/r/20200527232602.21596-1-nivedita@alum.mit.edu --- drivers/firmware/efi/libstub/x86-stub.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 99a0cfb0c7ad..5a48d996ed71 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -768,10 +768,12 @@ unsigned long efi_main(efi_handle_t handle, efi_err("Failed to load initrd!\n"); goto fail; } - efi_set_u64_split(addr, &hdr->ramdisk_image, - &boot_params->ext_ramdisk_image); - efi_set_u64_split(size, &hdr->ramdisk_size, - &boot_params->ext_ramdisk_size); + if (size > 0) { + efi_set_u64_split(addr, &hdr->ramdisk_image, + &boot_params->ext_ramdisk_image); + efi_set_u64_split(size, &hdr->ramdisk_size, + &boot_params->ext_ramdisk_size); + } } /* -- cgit v1.2.3-70-g09d2