From 019512f1fd373632577ecd3e8d721e48b71fb36a Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:33 -0400 Subject: [PATCH 01/44] 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(-) 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); } From eed4e0193ee7b00213791f7732bce0fc0c54f3b3 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:34 -0400 Subject: [PATCH 02/44] 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(-) 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; From 793473c28a4b17ade3c2bb69782fb0b55acb6e0b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:35 -0400 Subject: [PATCH 03/44] 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(-) 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; } From 36bdd0a78d56831f5d8df740012b6cdc71c0c73d Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:36 -0400 Subject: [PATCH 04/44] 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(-) 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); } From 61eac6d92d351fc446c7a0ba3b55a9b8d9977e30 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:37 -0400 Subject: [PATCH 05/44] 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(-) 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) From dad5b324212ef261e9b399b7456a4c3df0a8c300 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:38 -0400 Subject: [PATCH 06/44] 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(-) 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; } From 3839ab85d0fabd8b51c7548c4c1bc35bdba8adca Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:39 -0400 Subject: [PATCH 07/44] 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(-) 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); From 987053a30016a7d9ab3e4ad973e7c51aeb1f1ef6 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:40 -0400 Subject: [PATCH 08/44] 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(-) 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); } /* From f61900fd0ebf6c6b91719d63272a54f4d11051df Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:41 -0400 Subject: [PATCH 09/44] 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 --- .../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(-) 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"); From 7dde67f2a23ac11962b17128d9a71698430ced95 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:42 -0400 Subject: [PATCH 10/44] 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(-) 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 From 055042bedd4e0af8186266b189b81e73e708b82b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Thu, 30 Apr 2020 14:28:43 -0400 Subject: [PATCH 11/44] 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(-) 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; + } } /* From de8c55208c3865d0532466097b0244fbea1d9089 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 4 May 2020 11:02:48 -0400 Subject: [PATCH 12/44] 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 | 16 +++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) 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 -#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 +#ifndef ARCH_HAS_EFISTUB_WRAPPERS + #define efi_is_native() (true) -#endif -#ifndef efi_table_attr +#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_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 { \ From f77767ed5f4d398b29119563155e4ece2dfeee13 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 4 May 2020 10:06:29 +0200 Subject: [PATCH 13/44] 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(+) 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 From 0b8d9fc9953fde4ea3eb3191d986ca2d01eb783c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 5 May 2020 10:27:18 +0200 Subject: [PATCH 14/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 19 +++++++------------ drivers/firmware/efi/libstub/efistub.h | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) 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; + while (*str) { + efi_char16_t ch[] = { *str++, L'\0' }; - for (s8 = str; *s8; s8++) { - efi_char16_t ch[2] = { 0 }; - - ch[0] = *s8; - if (*s8 == '\n') { - efi_char16_t nl[2] = { '\r', 0 }; - efi_char16_printk(nl); - } - - efi_char16_printk(ch); + 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); From 4026229934f6ca0cb44af7b9df00e647b2f1f787 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 30 Apr 2020 12:12:42 -0700 Subject: [PATCH 15/44] 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(-) 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. */ From 0b76735380224497e1c78a1bed606cbda83e3427 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:53 -0400 Subject: [PATCH 16/44] 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(+) 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) From cb8c90a08c7f2f761919190093fca0611c1cece4 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:54 -0400 Subject: [PATCH 17/44] 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(-) 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; } From fd0528a24958b7e8d55669cf50749776308d4c5a Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:55 -0400 Subject: [PATCH 18/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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; + } } } From 2c7d1e30e5884dc6f6727ecd9417491c9f321b59 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:56 -0400 Subject: [PATCH 19/44] 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 +- .../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 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; +} From bbf8e8b0fe0401e1952c77088692fd273a9635a1 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:57 -0400 Subject: [PATCH 20/44] 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(-) 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) \ From 29a2806653360f1ba0f7608ef64d65d441a575f7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:58 -0400 Subject: [PATCH 21/44] 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(-) 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; From ce5e3f909fc0ed67e58367b9c56a54c20a51080b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:06:59 -0400 Subject: [PATCH 22/44] 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(-) 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); } From 3b8350959cba0df4070741618e50c367319793bb Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:00 -0400 Subject: [PATCH 23/44] 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(-) 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; From 77e48db04a02ebd00229281c26575979b0b465e0 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:01 -0400 Subject: [PATCH 24/44] 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(-) 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 */ From 7c30fd79168aeb83d11260246d93f8a293052007 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:02 -0400 Subject: [PATCH 25/44] 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(-) 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); From 3fbcf75bb41a0ff02189f6e873ba4c5d7c54c6a9 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:03 -0400 Subject: [PATCH 26/44] 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(-) 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; } From dec6119952eab7257624de5bd116a9e301ead5b7 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:04 -0400 Subject: [PATCH 27/44] 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(-) 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); } From fb031937a86874e6d663542bdbd83e310c13610e Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:05 -0400 Subject: [PATCH 28/44] 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(+) 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)) From 6c4bcd8a46a98856c06ca3ba8a80f03a61e23960 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:06 -0400 Subject: [PATCH 29/44] 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(-) 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'; From f97ca2c816748e3b7dee58775632f9e9269071e6 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:07 -0400 Subject: [PATCH 30/44] 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(-) 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); From 8fb331e10b63888e944a8a0dcf79b17e93b475ba Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:08 -0400 Subject: [PATCH 31/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 6 ++- drivers/firmware/efi/libstub/vsprintf.c | 42 +++++++++++-------- 2 files changed, 30 insertions(+), 18 deletions(-) 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; } From 23d5b73fbfd787c4c63841912b079eac000a7414 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Wed, 20 May 2020 19:07:54 +0200 Subject: [PATCH 32/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 29 +++++++++++++++++-- drivers/firmware/efi/libstub/efistub.h | 14 +++++---- 2 files changed, 35 insertions(+), 8 deletions(-) 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 From 9b47c5275614a16fd64359fab73fe6c736bf57a0 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:10 -0400 Subject: [PATCH 33/44] 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(-) 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; From 14c574f35cfbc9272fc67b41f074c847db139652 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:11 -0400 Subject: [PATCH 34/44] 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 + .../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(-) 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))) From d850a2ff918be0691f8d0544a2156c856c42da5b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:12 -0400 Subject: [PATCH 35/44] 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(-) 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(' '); From 4b75bd363de3f3429c62359be4e4aa4da7fa233b Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:13 -0400 Subject: [PATCH 36/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 67 +++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) 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; From a713979e443d4036e76e13bb3d30e9fa248915aa Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:14 -0400 Subject: [PATCH 37/44] 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(-) 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; } From 15c316bcbc23b777eae4e21f129526190aa20af1 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:15 -0400 Subject: [PATCH 38/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) 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++; + } + } } } From 04b24409b42a9fcae2379a6ddcf9c827b9af7a45 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 18 May 2020 15:07:16 -0400 Subject: [PATCH 39/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 66 ++----------------- 1 file changed, 6 insertions(+), 60 deletions(-) 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; From 80b1bfe1cb2f20225fb2d8f11d44af4a9d765396 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Wed, 20 May 2020 20:29:21 -0400 Subject: [PATCH 40/44] 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 --- .../firmware/efi/libstub/efi-stub-helper.c | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) 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 */ From 27cd5511feffea3956d081585316272795e45bce Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 19 May 2020 10:43:01 +0200 Subject: [PATCH 41/44] 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(-) 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; From d8bd8c6e2cfab8b78b537715255be8d7557791c0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 22 May 2020 18:15:49 +0200 Subject: [PATCH 42/44] 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(-) 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)) { From 6e99d3213b10ee18428d6d20715ad6c0e89ead4d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 23 May 2020 11:01:57 +0200 Subject: [PATCH 43/44] 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(-) 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) From 9241dfe7f2772fc73c82eb950afb1c795d2c012c Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Sat, 23 May 2020 18:15:13 -0400 Subject: [PATCH 44/44] efi/x86: Drop the special GDT for the EFI thunk Instead of using efi_gdt64 to switch back to 64-bit mode and then switching to the real boot-time GDT, just switch to the boot-time GDT directly. The two GDT's are identical other than efi_gdt64 not including the 32-bit code segment. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200523221513.1642948-1-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/efi_thunk_64.S | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/arch/x86/boot/compressed/efi_thunk_64.S b/arch/x86/boot/compressed/efi_thunk_64.S index 2b2049259619..c4bb0f9363f5 100644 --- a/arch/x86/boot/compressed/efi_thunk_64.S +++ b/arch/x86/boot/compressed/efi_thunk_64.S @@ -28,8 +28,6 @@ SYM_FUNC_START(__efi64_thunk) push %rbx leaq 1f(%rip), %rbp - leaq efi_gdt64(%rip), %rbx - movl %ebx, 2(%rbx) /* Fixup the gdt base address */ movl %ds, %eax push %rax @@ -48,7 +46,8 @@ SYM_FUNC_START(__efi64_thunk) movl %r8d, 0xc(%rsp) movl %r9d, 0x10(%rsp) - sgdt 0x14(%rsp) + leaq 0x14(%rsp), %rbx + sgdt (%rbx) /* * Switch to gdt with 32-bit segments. This is the firmware GDT @@ -68,8 +67,7 @@ SYM_FUNC_START(__efi64_thunk) pushq %rax lretq -1: lgdt 0x14(%rsp) - addq $32, %rsp +1: addq $32, %rsp movq %rdi, %rax pop %rbx @@ -175,14 +173,3 @@ SYM_DATA_END(efi32_boot_cs) SYM_DATA_START(efi32_boot_ds) .word 0 SYM_DATA_END(efi32_boot_ds) - -SYM_DATA_START(efi_gdt64) - .word efi_gdt64_end - efi_gdt64 - .long 0 /* Filled out by user */ - .word 0 - .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x00af9a000000ffff /* __KERNEL_CS */ - .quad 0x00cf92000000ffff /* __KERNEL_DS */ - .quad 0x0080890000000000 /* TS descriptor */ - .quad 0x0000000000000000 /* TS continued */ -SYM_DATA_END_LABEL(efi_gdt64, SYM_L_LOCAL, efi_gdt64_end)