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/firmware') 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 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/firmware') 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 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 --- drivers/firmware/efi/libstub/efistub.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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/firmware') 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 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 --- drivers/firmware/efi/libstub/efistub.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') 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 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 --- drivers/firmware/efi/libstub/efi-stub-helper.c | 3 + drivers/firmware/efi/libstub/efistub.h | 2 + drivers/firmware/efi/libstub/gop.c | 107 +++++++++++++++++++++++++ 3 files changed, 112 insertions(+) (limited to 'drivers/firmware') 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 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 --- drivers/firmware/efi/libstub/gop.c | 84 +++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') 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; +