From 6d0fd536183034953bf84826fecb37e47779d24b Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 28 Aug 2020 10:20:31 -0700 Subject: [PATCH 01/26] include: pe.h: Add RISC-V related PE definition Define RISC-V related machine types. Signed-off-by: Atish Patra Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20200415195422.19866-3-atish.patra@wdc.com Signed-off-by: Ard Biesheuvel --- include/linux/pe.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/pe.h b/include/linux/pe.h index 8ad71d763a77..daf09ffffe38 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -55,6 +55,9 @@ #define IMAGE_FILE_MACHINE_POWERPC 0x01f0 #define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 #define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_RISCV32 0x5032 +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 +#define IMAGE_FILE_MACHINE_RISCV128 0x5128 #define IMAGE_FILE_MACHINE_SH3 0x01a2 #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH3E 0x01a4 From f30f242fb1319e616fbcf94a43195a1c57db99b8 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Wed, 19 Aug 2020 15:24:23 -0700 Subject: [PATCH 02/26] efi: Rename arm-init to efi-init common for all arch arm-init is responsible for setting up efi runtime and doesn't actually do any ARM specific stuff. RISC-V can use the same source code as it is. Rename it to efi-init so that RISC-V can use it. Signed-off-by: Atish Patra Link: https://lore.kernel.org/r/20200819222425.30721-8-atish.patra@wdc.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Makefile | 2 +- drivers/firmware/efi/{arm-init.c => efi-init.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/firmware/efi/{arm-init.c => efi-init.c} (100%) diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 7a216984552b..61fd1e8b26fb 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -32,7 +32,7 @@ obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o -arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o +arm-obj-$(CONFIG_EFI) := efi-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/efi-init.c similarity index 100% rename from drivers/firmware/efi/arm-init.c rename to drivers/firmware/efi/efi-init.c From 6208857b8f7ebdfe84e1be7573be4552a5896a0d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 10 Sep 2020 17:09:45 +0300 Subject: [PATCH 03/26] efi/libstub: arm32: Base FDT and initrd placement on image address The way we use the base of DRAM in the EFI stub is problematic as it is ill defined what the base of DRAM actually means. There are some restrictions on the placement of FDT and initrd which are defined in terms of dram_base, but given that the placement of the kernel in memory is what defines these boundaries (as on ARM, this is where the linear region starts), it is better to use the image address in these cases, and disregard dram_base altogether. Reviewed-by: Maxim Uvarov Tested-by: Maxim Uvarov Signed-off-by: Ard Biesheuvel --- arch/arm/include/asm/efi.h | 23 +++++++++++------------ arch/arm64/include/asm/efi.h | 5 ++--- drivers/firmware/efi/libstub/arm32-stub.c | 2 +- drivers/firmware/efi/libstub/efi-stub.c | 4 ++-- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h index 5dcf3c6011b7..3ee4f4381985 100644 --- a/arch/arm/include/asm/efi.h +++ b/arch/arm/include/asm/efi.h @@ -66,25 +66,24 @@ static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) #define MAX_UNCOMP_KERNEL_SIZE SZ_32M /* - * The kernel zImage should preferably be located between 32 MB and 128 MB - * from the base of DRAM. The min address leaves space for a maximal size - * uncompressed image, and the max address is due to how the zImage decompressor - * picks a destination address. + * phys-to-virt patching requires that the physical to virtual offset fits + * into the immediate field of an add/sub instruction, which comes down to the + * 24 least significant bits being zero, and so the offset should be a multiple + * of 16 MB. Since PAGE_OFFSET itself is a multiple of 16 MB, the physical + * base should be aligned to 16 MB as well. */ -#define ZIMAGE_OFFSET_LIMIT SZ_128M -#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE +#define EFI_PHYS_ALIGN SZ_16M -/* on ARM, the FDT should be located in the first 128 MB of RAM */ -static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base) +/* on ARM, the FDT should be located in a lowmem region */ +static inline unsigned long efi_get_max_fdt_addr(unsigned long image_addr) { - return dram_base + ZIMAGE_OFFSET_LIMIT; + return round_down(image_addr, EFI_PHYS_ALIGN) + SZ_512M; } /* on ARM, the initrd should be loaded in a lowmem region */ -static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base, - unsigned long image_addr) +static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) { - return dram_base + SZ_512M; + return round_down(image_addr, EFI_PHYS_ALIGN) + SZ_512M; } struct efi_arm_entry_state { diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index d4ab3f73e7a3..973b14415271 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -65,7 +65,7 @@ efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); (SEGMENT_ALIGN > THREAD_ALIGN ? SEGMENT_ALIGN : THREAD_ALIGN) /* on arm64, the FDT may be located anywhere in system RAM */ -static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base) +static inline unsigned long efi_get_max_fdt_addr(unsigned long image_addr) { return ULONG_MAX; } @@ -80,8 +80,7 @@ static inline unsigned long efi_get_max_fdt_addr(unsigned long dram_base) * apply to other bootloaders, and are required for some kernel * configurations. */ -static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base, - unsigned long image_addr) +static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) { return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1)); } diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index d08e5d55838c..bcf770c2ce02 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -252,7 +252,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_status_t status; /* use a 16 MiB aligned base for the decompressed kernel */ - kernel_base = round_up(dram_base, SZ_16M) + TEXT_OFFSET; + kernel_base = round_up(dram_base, EFI_PHYS_ALIGN) + TEXT_OFFSET; /* * Note that some platforms (notably, the Raspberry Pi 2) put diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index a5a405d8ab44..65a0070010a1 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -262,7 +262,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_info("Generating empty DTB\n"); if (!efi_noinitrd) { - max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + max_addr = efi_get_max_initrd_addr(image_addr); status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, max_addr); if (status != EFI_SUCCESS) @@ -306,7 +306,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, install_memreserve_table(); status = allocate_new_fdt_and_exit_boot(handle, &fdt_addr, - efi_get_max_fdt_addr(dram_base), + efi_get_max_fdt_addr(image_addr), initrd_addr, initrd_size, cmdline_ptr, fdt_addr, fdt_size); if (status != EFI_SUCCESS) From 1a895dbf4b66456bfb7da646cc9b1be3e24f4a1d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 9 Sep 2020 16:16:20 +0300 Subject: [PATCH 04/26] efi/libstub: Export efi_low_alloc_above() to other units Permit arm32-stub.c to access efi_low_alloc_above() in a subsequent patch by giving it external linkage and declaring it in efistub.h. Reviewed-by: Maxim Uvarov Tested-by: Maxim Uvarov Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 3 +++ drivers/firmware/efi/libstub/relocate.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 85050f5a1b28..158f86f1f9fc 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -740,6 +740,9 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, unsigned long max, unsigned long align); +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min); + efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, unsigned long alloc_size, diff --git a/drivers/firmware/efi/libstub/relocate.c b/drivers/firmware/efi/libstub/relocate.c index 9b1aaf8b123f..8ee9eb2b9039 100644 --- a/drivers/firmware/efi/libstub/relocate.c +++ b/drivers/firmware/efi/libstub/relocate.c @@ -20,8 +20,8 @@ * * Return: status code */ -static efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min) +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) { unsigned long map_size, desc_size, buff_size; efi_memory_desc_t *map; From 762cd288fc4a24a372f36408e69b1885967f94bb Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 9 Sep 2020 17:11:50 +0300 Subject: [PATCH 05/26] efi/libstub: arm32: Use low allocation for the uncompressed kernel Before commit d0f9ca9be11f25ef ("ARM: decompressor: run decompressor in place if loaded via UEFI") we were rather limited in the choice of base address for the uncompressed kernel, as we were relying on the logic in the decompressor that blindly rounds down the decompressor execution address to the next multiple of 128 MiB, and decompresses the kernel there. For this reason, we have a lot of complicated memory region handling code, to ensure that this memory window is available, even though it could be occupied by reserved regions or other allocations that may or may not collide with the uncompressed image. Today, we simply pass the target address for the decompressed image to the decompressor directly, and so we can choose a suitable window just by finding a 16 MiB aligned region, while taking TEXT_OFFSET and the region for the swapper page tables into account. So let's get rid of the complicated logic, and instead, use the existing bottom up allocation routine to allocate a suitable window as low as possible, and carve out a memory region that has the right properties. Note that this removes any dependencies on the 'dram_base' argument to handle_kernel_image(), and so this is removed as well. Given that this was the only remaining use of dram_base, the code that produces it is removed entirely as well. Reviewed-by: Maxim Uvarov Tested-by: Maxim Uvarov Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm32-stub.c | 178 +++++----------------- drivers/firmware/efi/libstub/arm64-stub.c | 1 - drivers/firmware/efi/libstub/efi-stub.c | 44 +----- drivers/firmware/efi/libstub/efistub.h | 4 - 4 files changed, 38 insertions(+), 189 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index bcf770c2ce02..4b5b2403b3a0 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -113,162 +113,58 @@ void free_screen_info(struct screen_info *si) efi_bs_call(free_pool, si); } -static efi_status_t reserve_kernel_base(unsigned long dram_base, - unsigned long *reserve_addr, - unsigned long *reserve_size) -{ - efi_physical_addr_t alloc_addr; - efi_memory_desc_t *memory_map; - unsigned long nr_pages, map_size, desc_size, buff_size; - efi_status_t status; - unsigned long l; - - struct efi_boot_memmap map = { - .map = &memory_map, - .map_size = &map_size, - .desc_size = &desc_size, - .desc_ver = NULL, - .key_ptr = NULL, - .buff_size = &buff_size, - }; - - /* - * Reserve memory for the uncompressed kernel image. This is - * all that prevents any future allocations from conflicting - * with the kernel. Since we can't tell from the compressed - * image how much DRAM the kernel actually uses (due to BSS - * size uncertainty) we allocate the maximum possible size. - * Do this very early, as prints can cause memory allocations - * that may conflict with this. - */ - alloc_addr = dram_base + MAX_UNCOMP_KERNEL_SIZE; - nr_pages = MAX_UNCOMP_KERNEL_SIZE / EFI_PAGE_SIZE; - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, - EFI_BOOT_SERVICES_DATA, nr_pages, &alloc_addr); - if (status == EFI_SUCCESS) { - if (alloc_addr == dram_base) { - *reserve_addr = alloc_addr; - *reserve_size = MAX_UNCOMP_KERNEL_SIZE; - return EFI_SUCCESS; - } - /* - * If we end up here, the allocation succeeded but starts below - * dram_base. This can only occur if the real base of DRAM is - * not a multiple of 128 MB, in which case dram_base will have - * been rounded up. Since this implies that a part of the region - * was already occupied, we need to fall through to the code - * below to ensure that the existing allocations don't conflict. - * For this reason, we use EFI_BOOT_SERVICES_DATA above and not - * EFI_LOADER_DATA, which we wouldn't able to distinguish from - * allocations that we want to disallow. - */ - } - - /* - * If the allocation above failed, we may still be able to proceed: - * if the only allocations in the region are of types that will be - * released to the OS after ExitBootServices(), the decompressor can - * safely overwrite them. - */ - status = efi_get_memory_map(&map); - if (status != EFI_SUCCESS) { - efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n"); - return status; - } - - for (l = 0; l < map_size; l += desc_size) { - efi_memory_desc_t *desc; - u64 start, end; - - desc = (void *)memory_map + l; - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - /* Skip if entry does not intersect with region */ - if (start >= dram_base + MAX_UNCOMP_KERNEL_SIZE || - end <= dram_base) - continue; - - switch (desc->type) { - case EFI_BOOT_SERVICES_CODE: - case EFI_BOOT_SERVICES_DATA: - /* Ignore types that are released to the OS anyway */ - continue; - - case EFI_CONVENTIONAL_MEMORY: - /* Skip soft reserved conventional memory */ - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - /* - * Reserve the intersection between this entry and the - * region. - */ - start = max(start, (u64)dram_base); - end = min(end, (u64)dram_base + MAX_UNCOMP_KERNEL_SIZE); - - status = efi_bs_call(allocate_pages, - EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, - (end - start) / EFI_PAGE_SIZE, - &start); - if (status != EFI_SUCCESS) { - efi_err("reserve_kernel_base(): alloc failed.\n"); - goto out; - } - break; - - case EFI_LOADER_CODE: - case EFI_LOADER_DATA: - /* - * These regions may be released and reallocated for - * another purpose (including EFI_RUNTIME_SERVICE_DATA) - * at any time during the execution of the OS loader, - * so we cannot consider them as safe. - */ - default: - /* - * Treat any other allocation in the region as unsafe */ - status = EFI_OUT_OF_RESOURCES; - goto out; - } - } - - status = EFI_SUCCESS; -out: - efi_bs_call(free_pool, memory_map); - return status; -} - efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image) { - unsigned long kernel_base; + const int slack = TEXT_OFFSET - 5 * PAGE_SIZE; + int alloc_size = MAX_UNCOMP_KERNEL_SIZE + EFI_PHYS_ALIGN; + unsigned long alloc_base, kernel_base; efi_status_t status; - /* use a 16 MiB aligned base for the decompressed kernel */ - kernel_base = round_up(dram_base, EFI_PHYS_ALIGN) + TEXT_OFFSET; - /* - * Note that some platforms (notably, the Raspberry Pi 2) put - * spin-tables and other pieces of firmware at the base of RAM, - * abusing the fact that the window of TEXT_OFFSET bytes at the - * base of the kernel image is only partially used at the moment. - * (Up to 5 pages are used for the swapper page tables) + * Allocate space for the decompressed kernel as low as possible. + * The region should be 16 MiB aligned, but the first 'slack' bytes + * are not used by Linux, so we allow those to be occupied by the + * firmware. */ - status = reserve_kernel_base(kernel_base - 5 * PAGE_SIZE, reserve_addr, - reserve_size); + status = efi_low_alloc_above(alloc_size, EFI_PAGE_SIZE, &alloc_base, 0x0); if (status != EFI_SUCCESS) { efi_err("Unable to allocate memory for uncompressed kernel.\n"); return status; } - *image_addr = kernel_base; + if ((alloc_base % EFI_PHYS_ALIGN) > slack) { + /* + * More than 'slack' bytes are already occupied at the base of + * the allocation, so we need to advance to the next 16 MiB block. + */ + kernel_base = round_up(alloc_base, EFI_PHYS_ALIGN); + efi_info("Free memory starts at 0x%lx, setting kernel_base to 0x%lx\n", + alloc_base, kernel_base); + } else { + kernel_base = round_down(alloc_base, EFI_PHYS_ALIGN); + } + + *reserve_addr = kernel_base + slack; + *reserve_size = MAX_UNCOMP_KERNEL_SIZE; + + /* now free the parts that we will not use */ + if (*reserve_addr > alloc_base) { + efi_bs_call(free_pages, alloc_base, + (*reserve_addr - alloc_base) / EFI_PAGE_SIZE); + alloc_size -= *reserve_addr - alloc_base; + } + efi_bs_call(free_pages, *reserve_addr + MAX_UNCOMP_KERNEL_SIZE, + (alloc_size - MAX_UNCOMP_KERNEL_SIZE) / EFI_PAGE_SIZE); + + *image_addr = kernel_base + TEXT_OFFSET; *image_size = 0; + + efi_debug("image addr == 0x%lx, reserve_addr == 0x%lx\n", + *image_addr, *reserve_addr); + return EFI_SUCCESS; } diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index e5bfac79e5ac..56e79f3ebd67 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -50,7 +50,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image) { efi_status_t status; diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 65a0070010a1..311a16802dd6 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -87,40 +87,6 @@ static void install_memreserve_table(void) efi_err("Failed to install memreserve config table!\n"); } -static unsigned long get_dram_base(void) -{ - efi_status_t status; - unsigned long map_size, buff_size; - unsigned long membase = EFI_ERROR; - struct efi_memory_map map; - efi_memory_desc_t *md; - struct efi_boot_memmap boot_map; - - boot_map.map = (efi_memory_desc_t **)&map.map; - boot_map.map_size = &map_size; - boot_map.desc_size = &map.desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - return membase; - - map.map_end = map.map + map_size; - - for_each_efi_memory_desc_in_map(&map, md) { - if (md->attribute & EFI_MEMORY_WB) { - if (membase > md->phys_addr) - membase = md->phys_addr; - } - } - - efi_bs_call(free_pool, map.map); - - return membase; -} - /* * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint * that is described in the PE/COFF header. Most of the code is the same @@ -134,7 +100,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_status_t status; unsigned long image_addr; unsigned long image_size = 0; - unsigned long dram_base; /* addr/point and size pairs for memory management*/ unsigned long initrd_addr = 0; unsigned long initrd_size = 0; @@ -174,13 +139,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, goto fail; } - dram_base = get_dram_base(); - if (dram_base == EFI_ERROR) { - efi_err("Failed to find DRAM base\n"); - status = EFI_LOAD_ERROR; - goto fail; - } - /* * Get the command line from EFI, using the LOADED_IMAGE * protocol. We are going to copy the command line into the @@ -218,7 +176,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, status = handle_kernel_image(&image_addr, &image_size, &reserve_addr, &reserve_size, - dram_base, image); + image); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); goto fail_free_screeninfo; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 158f86f1f9fc..27cdcb11714d 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -10,9 +10,6 @@ #include #include -/* error code which can't be mistaken for valid address */ -#define EFI_ERROR (~0UL) - /* * __init annotations should not be used in the EFI stub, since the code is * either included in the decompressor (x86, ARM) where they have no effect, @@ -789,7 +786,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, unsigned long *reserve_size, - unsigned long dram_base, efi_loaded_image_t *image); asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, From 5c4c30f40ca246f83b6663984dcdcbfeb0f8b66f Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Wed, 9 Sep 2020 14:44:32 +0800 Subject: [PATCH 06/26] efi/printf: remove unneeded semicolon Fix the warning below. efi/libstub/vsprintf.c:135:2-3: Unneeded semicolon Signed-off-by: Tian Tao Acked-by: Arvind Sankar Link: https://lore.kernel.org/r/1599633872-36784-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/vsprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/vsprintf.c b/drivers/firmware/efi/libstub/vsprintf.c index e65ef49a54cd..1088e288c04d 100644 --- a/drivers/firmware/efi/libstub/vsprintf.c +++ b/drivers/firmware/efi/libstub/vsprintf.c @@ -135,7 +135,7 @@ char *number(char *end, unsigned long long num, int base, char locase) break; default: unreachable(); - }; + } return end; } From 58c909022a5a56cd1d9e89c8c5461fd1f6a27bb5 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 4 Sep 2020 21:31:05 -0400 Subject: [PATCH 07/26] efi: Support for MOK variable config table Because of system-specific EFI firmware limitations, EFI volatile variables may not be capable of holding the required contents of the Machine Owner Key (MOK) certificate store when the certificate list grows above some size. Therefore, an EFI boot loader may pass the MOK certs via a EFI configuration table created specifically for this purpose to avoid this firmware limitation. An EFI configuration table is a much more primitive mechanism compared to EFI variables and is well suited for one-way passage of static information from a pre-OS environment to the kernel. This patch adds initial kernel support to recognize, parse, and validate the EFI MOK configuration table, where named entries contain the same data that would otherwise be provided in similarly named EFI variables. Additionally, this patch creates a sysfs binary file for each EFI MOK configuration table entry found. These files are read-only to root and are provided for use by user space utilities such as mokutil. A subsequent patch will load MOK certs into the trusted platform key ring using this infrastructure. Signed-off-by: Lenny Szubowicz Link: https://lore.kernel.org/r/20200905013107.10457-2-lszubowi@redhat.com Signed-off-by: Ard Biesheuvel --- arch/x86/kernel/setup.c | 1 + arch/x86/platform/efi/efi.c | 3 + drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efi-init.c | 1 + drivers/firmware/efi/efi.c | 6 + drivers/firmware/efi/mokvar-table.c | 360 ++++++++++++++++++++++++++++ include/linux/efi.h | 34 +++ 7 files changed, 406 insertions(+) create mode 100644 drivers/firmware/efi/mokvar-table.c diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 3511736fbc74..d41be0df72f8 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1077,6 +1077,7 @@ void __init setup_arch(char **cmdline_p) efi_fake_memmap(); efi_find_mirror(); efi_esrt_init(); + efi_mokvar_table_init(); /* * The EFI specification says that boot service code won't be diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f6ea8f1a9d57..6961bedc6503 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -91,6 +91,9 @@ static const unsigned long * const efi_tables[] = { &efi.tpm_log, &efi.tpm_final_log, &efi_rng_seed, +#ifdef CONFIG_LOAD_UEFI_KEYS + &efi.mokvar_table, +#endif }; u64 efi_setup; /* efi setup_data physical address */ diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 61fd1e8b26fb..e8da782280b6 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o +obj-$(CONFIG_LOAD_UEFI_KEYS) += mokvar-table.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 71c445d20258..f55a92ff12c0 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -236,6 +236,7 @@ void __init efi_init(void) reserve_regions(); efi_esrt_init(); + efi_mokvar_table_init(); memblock_reserve(data.phys_map & PAGE_MASK, PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK))); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index fdd1db025dbf..820f5b1dfba3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -43,6 +43,9 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, +#ifdef CONFIG_LOAD_UEFI_KEYS + .mokvar_table = EFI_INVALID_TABLE_ADDR, +#endif }; EXPORT_SYMBOL(efi); @@ -516,6 +519,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { {EFI_RT_PROPERTIES_TABLE_GUID, &rt_prop, "RTPROP" }, #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, &rci2_table_phys }, +#endif +#ifdef CONFIG_LOAD_UEFI_KEYS + {LINUX_EFI_MOK_VARIABLE_TABLE_GUID, &efi.mokvar_table, "MOKvar" }, #endif {}, }; diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c new file mode 100644 index 000000000000..b1cd49893d4d --- /dev/null +++ b/drivers/firmware/efi/mokvar-table.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mokvar-table.c + * + * Copyright (c) 2020 Red Hat + * Author: Lenny Szubowicz + * + * This module contains the kernel support for the Linux EFI Machine + * Owner Key (MOK) variable configuration table, which is identified by + * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID. + * + * This EFI configuration table provides a more robust alternative to + * EFI volatile variables by which an EFI boot loader can pass the + * contents of the Machine Owner Key (MOK) certificate stores to the + * kernel during boot. If both the EFI MOK config table and corresponding + * EFI MOK variables are present, the table should be considered as + * more authoritative. + * + * This module includes code that validates and maps the EFI MOK table, + * if it's presence was detected very early in boot. + * + * Kernel interface routines are provided to walk through all the + * entries in the MOK config table or to search for a specific named + * entry. + * + * The contents of the individual named MOK config table entries are + * made available to user space via read-only sysfs binary files under: + * + * /sys/firmware/efi/mok-variables/ + * + */ +#define pr_fmt(fmt) "mokvar: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed + * sequence of struct efi_mokvar_table_entry, one for each named + * MOK variable. The sequence is terminated by an entry with a + * completely NULL name and 0 data size. + * + * efi_mokvar_table_size is set to the computed size of the + * MOK config table by efi_mokvar_table_init(). This will be + * non-zero if and only if the table if present and has been + * validated by efi_mokvar_table_init(). + */ +static size_t efi_mokvar_table_size; + +/* + * efi_mokvar_table_va is the kernel virtual address at which the + * EFI MOK config table has been mapped by efi_mokvar_sysfs_init(). + */ +static struct efi_mokvar_table_entry *efi_mokvar_table_va; + +/* + * Each /sys/firmware/efi/mok-variables/ sysfs file is represented by + * an instance of struct efi_mokvar_sysfs_attr on efi_mokvar_sysfs_list. + * bin_attr.private points to the associated EFI MOK config table entry. + * + * This list is created during boot and then remains unchanged. + * So no synchronization is currently required to walk the list. + */ +struct efi_mokvar_sysfs_attr { + struct bin_attribute bin_attr; + struct list_head node; +}; + +static LIST_HEAD(efi_mokvar_sysfs_list); +static struct kobject *mokvar_kobj; + +/* + * efi_mokvar_table_init() - Early boot validation of EFI MOK config table + * + * If present, validate and compute the size of the EFI MOK variable + * configuration table. This table may be provided by an EFI boot loader + * as an alternative to ordinary EFI variables, due to platform-dependent + * limitations. The memory occupied by this table is marked as reserved. + * + * This routine must be called before efi_free_boot_services() in order + * to guarantee that it can mark the table as reserved. + * + * Implicit inputs: + * efi.mokvar_table: Physical address of EFI MOK variable config table + * or special value that indicates no such table. + * + * Implicit outputs: + * efi_mokvar_table_size: Computed size of EFI MOK variable config table. + * The table is considered present and valid if this + * is non-zero. + */ +void __init efi_mokvar_table_init(void) +{ + efi_memory_desc_t md; + u64 end_pa; + void *va = NULL; + size_t cur_offset = 0; + size_t offset_limit; + size_t map_size = 0; + size_t map_size_needed = 0; + size_t size; + struct efi_mokvar_table_entry *mokvar_entry; + int err = -EINVAL; + + if (!efi_enabled(EFI_MEMMAP)) + return; + + if (efi.mokvar_table == EFI_INVALID_TABLE_ADDR) + return; + /* + * The EFI MOK config table must fit within a single EFI memory + * descriptor range. + */ + err = efi_mem_desc_lookup(efi.mokvar_table, &md); + if (err) { + pr_warn("EFI MOKvar config table is not within the EFI memory map\n"); + return; + } + end_pa = efi_mem_desc_end(&md); + if (efi.mokvar_table >= end_pa) { + pr_err("EFI memory descriptor containing MOKvar config table is invalid\n"); + return; + } + offset_limit = end_pa - efi.mokvar_table; + /* + * Validate the MOK config table. Since there is no table header + * from which we could get the total size of the MOK config table, + * we compute the total size as we validate each variably sized + * entry, remapping as necessary. + */ + while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) { + mokvar_entry = va + cur_offset; + map_size_needed = cur_offset + sizeof(*mokvar_entry); + if (map_size_needed > map_size) { + if (va) + early_memunmap(va, map_size); + /* + * Map a little more than the fixed size entry + * header, anticipating some data. It's safe to + * do so as long as we stay within current memory + * descriptor. + */ + map_size = min(map_size_needed + 2*EFI_PAGE_SIZE, + offset_limit); + va = early_memremap(efi.mokvar_table, map_size); + if (!va) { + pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%zu.\n", + efi.mokvar_table, map_size); + return; + } + mokvar_entry = va + cur_offset; + } + + /* Check for last sentinel entry */ + if (mokvar_entry->name[0] == '\0') { + if (mokvar_entry->data_size != 0) + break; + err = 0; + break; + } + + /* Sanity check that the name is null terminated */ + size = strnlen(mokvar_entry->name, + sizeof(mokvar_entry->name)); + if (size >= sizeof(mokvar_entry->name)) + break; + + /* Advance to the next entry */ + cur_offset = map_size_needed + mokvar_entry->data_size; + } + + if (va) + early_memunmap(va, map_size); + if (err) { + pr_err("EFI MOKvar config table is not valid\n"); + return; + } + efi_mem_reserve(efi.mokvar_table, map_size_needed); + efi_mokvar_table_size = map_size_needed; +} + +/* + * efi_mokvar_entry_next() - Get next entry in the EFI MOK config table + * + * mokvar_entry: Pointer to current EFI MOK config table entry + * or null. Null indicates get first entry. + * Passed by reference. This is updated to the + * same value as the return value. + * + * Returns: Pointer to next EFI MOK config table entry + * or null, if there are no more entries. + * Same value is returned in the mokvar_entry + * parameter. + * + * This routine depends on the EFI MOK config table being entirely + * mapped with it's starting virtual address in efi_mokvar_table_va. + */ +struct efi_mokvar_table_entry *efi_mokvar_entry_next( + struct efi_mokvar_table_entry **mokvar_entry) +{ + struct efi_mokvar_table_entry *mokvar_cur; + struct efi_mokvar_table_entry *mokvar_next; + size_t size_cur; + + mokvar_cur = *mokvar_entry; + *mokvar_entry = NULL; + + if (efi_mokvar_table_va == NULL) + return NULL; + + if (mokvar_cur == NULL) { + mokvar_next = efi_mokvar_table_va; + } else { + if (mokvar_cur->name[0] == '\0') + return NULL; + size_cur = sizeof(*mokvar_cur) + mokvar_cur->data_size; + mokvar_next = (void *)mokvar_cur + size_cur; + } + + if (mokvar_next->name[0] == '\0') + return NULL; + + *mokvar_entry = mokvar_next; + return mokvar_next; +} + +/* + * efi_mokvar_entry_find() - Find EFI MOK config entry by name + * + * name: Name of the entry to look for. + * + * Returns: Pointer to EFI MOK config table entry if found; + * null otherwise. + * + * This routine depends on the EFI MOK config table being entirely + * mapped with it's starting virtual address in efi_mokvar_table_va. + */ +struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name) +{ + struct efi_mokvar_table_entry *mokvar_entry = NULL; + + while (efi_mokvar_entry_next(&mokvar_entry)) { + if (!strncmp(name, mokvar_entry->name, + sizeof(mokvar_entry->name))) + return mokvar_entry; + } + return NULL; +} + +/* + * efi_mokvar_sysfs_read() - sysfs binary file read routine + * + * Returns: Count of bytes read. + * + * Copy EFI MOK config table entry data for this mokvar sysfs binary file + * to the supplied buffer, starting at the specified offset into mokvar table + * entry data, for the specified count bytes. The copy is limited by the + * amount of data in this mokvar config table entry. + */ +static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private; + + if (!capable(CAP_SYS_ADMIN)) + return 0; + + if (off >= mokvar_entry->data_size) + return 0; + if (count > mokvar_entry->data_size - off) + count = mokvar_entry->data_size - off; + + memcpy(buf, mokvar_entry->data + off, count); + return count; +} + +/* + * efi_mokvar_sysfs_init() - Map EFI MOK config table and create sysfs + * + * Map the EFI MOK variable config table for run-time use by the kernel + * and create the sysfs entries in /sys/firmware/efi/mok-variables/ + * + * This routine just returns if a valid EFI MOK variable config table + * was not found earlier during boot. + * + * This routine must be called during a "middle" initcall phase, i.e. + * after efi_mokvar_table_init() but before UEFI certs are loaded + * during late init. + * + * Implicit inputs: + * efi.mokvar_table: Physical address of EFI MOK variable config table + * or special value that indicates no such table. + * + * efi_mokvar_table_size: Computed size of EFI MOK variable config table. + * The table is considered present and valid if this + * is non-zero. + * + * Implicit outputs: + * efi_mokvar_table_va: Start virtual address of the EFI MOK config table. + */ +static int __init efi_mokvar_sysfs_init(void) +{ + void *config_va; + struct efi_mokvar_table_entry *mokvar_entry = NULL; + struct efi_mokvar_sysfs_attr *mokvar_sysfs = NULL; + int err = 0; + + if (efi_mokvar_table_size == 0) + return -ENOENT; + + config_va = memremap(efi.mokvar_table, efi_mokvar_table_size, + MEMREMAP_WB); + if (!config_va) { + pr_err("Failed to map EFI MOKvar config table\n"); + return -ENOMEM; + } + efi_mokvar_table_va = config_va; + + mokvar_kobj = kobject_create_and_add("mok-variables", efi_kobj); + if (!mokvar_kobj) { + pr_err("Failed to create EFI mok-variables sysfs entry\n"); + return -ENOMEM; + } + + while (efi_mokvar_entry_next(&mokvar_entry)) { + mokvar_sysfs = kzalloc(sizeof(*mokvar_sysfs), GFP_KERNEL); + if (!mokvar_sysfs) { + err = -ENOMEM; + break; + } + + sysfs_bin_attr_init(&mokvar_sysfs->bin_attr); + mokvar_sysfs->bin_attr.private = mokvar_entry; + mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name; + mokvar_sysfs->bin_attr.attr.mode = 0400; + mokvar_sysfs->bin_attr.size = mokvar_entry->data_size; + mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read; + + err = sysfs_create_bin_file(mokvar_kobj, + &mokvar_sysfs->bin_attr); + if (err) + break; + + list_add_tail(&mokvar_sysfs->node, &efi_mokvar_sysfs_list); + } + + if (err) { + pr_err("Failed to create some EFI mok-variables sysfs entries\n"); + kfree(mokvar_sysfs); + } + return err; +} +device_initcall(efi_mokvar_sysfs_init); diff --git a/include/linux/efi.h b/include/linux/efi.h index 73db1ae04cef..4a2332f146eb 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -357,6 +357,7 @@ void efi_native_runtime_setup(void); #define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25) #define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2) #define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) +#define LINUX_EFI_MOK_VARIABLE_TABLE_GUID EFI_GUID(0xc451ed2b, 0x9694, 0x45d3, 0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89) /* OEM GUIDs */ #define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55) @@ -546,6 +547,7 @@ extern struct efi { unsigned long esrt; /* ESRT table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ + unsigned long mokvar_table; /* MOK variable config table */ efi_get_time_t *get_time; efi_set_time_t *set_time; @@ -1252,4 +1254,36 @@ void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size); char *efi_systab_show_arch(char *str); +/* + * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table can be provided + * to the kernel by an EFI boot loader. The table contains a packed + * sequence of these entries, one for each named MOK variable. + * The sequence is terminated by an entry with a completely NULL + * name and 0 data size. + */ +struct efi_mokvar_table_entry { + char name[256]; + u64 data_size; + u8 data[]; +} __attribute((packed)); + +#ifdef CONFIG_LOAD_UEFI_KEYS +extern void __init efi_mokvar_table_init(void); +extern struct efi_mokvar_table_entry *efi_mokvar_entry_next( + struct efi_mokvar_table_entry **mokvar_entry); +extern struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name); +#else +static inline void efi_mokvar_table_init(void) { } +static inline struct efi_mokvar_table_entry *efi_mokvar_entry_next( + struct efi_mokvar_table_entry **mokvar_entry) +{ + return NULL; +} +static inline struct efi_mokvar_table_entry *efi_mokvar_entry_find( + const char *name) +{ + return NULL; +} +#endif + #endif /* _LINUX_EFI_H */ From 38a1f03aa24094b4a8de846700cb6cb21cc06468 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 4 Sep 2020 21:31:06 -0400 Subject: [PATCH 08/26] integrity: Move import of MokListRT certs to a separate routine Move the loading of certs from the UEFI MokListRT into a separate routine to facilitate additional MokList functionality. There is no visible functional change as a result of this patch. Although the UEFI dbx certs are now loaded before the MokList certs, they are loaded onto different key rings. So the order of the keys on their respective key rings is the same. Signed-off-by: Lenny Szubowicz Reviewed-by: Mimi Zohar Link: https://lore.kernel.org/r/20200905013107.10457-3-lszubowi@redhat.com Signed-off-by: Ard Biesheuvel --- security/integrity/platform_certs/load_uefi.c | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index 253fb9a7fc98..c1c622b4dc78 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -66,6 +66,43 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, } /* + * load_moklist_certs() - Load MokList certs + * + * Load the certs contained in the UEFI MokListRT database into the + * platform trusted keyring. + * + * Return: Status + */ +static int __init load_moklist_certs(void) +{ + efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; + void *mok; + unsigned long moksize; + efi_status_t status; + int rc; + + /* Get MokListRT. It might not exist, so it isn't an error + * if we can't get it. + */ + mok = get_cert_list(L"MokListRT", &mok_var, &moksize, &status); + if (mok) { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + kfree(mok); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + return rc; + } + if (status == EFI_NOT_FOUND) + pr_debug("MokListRT variable wasn't found\n"); + else + pr_info("Couldn't get UEFI MokListRT\n"); + return 0; +} + +/* + * load_uefi_certs() - Load certs from UEFI sources + * * Load the certs contained in the UEFI databases into the platform trusted * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist * keyring. @@ -73,17 +110,16 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, static int __init load_uefi_certs(void) { efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; - efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; - void *db = NULL, *dbx = NULL, *mok = NULL; - unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + void *db = NULL, *dbx = NULL; + unsigned long dbsize = 0, dbxsize = 0; efi_status_t status; int rc = 0; if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) return false; - /* Get db, MokListRT, and dbx. They might not exist, so it isn't - * an error if we can't get them. + /* Get db and dbx. They might not exist, so it isn't an error + * if we can't get them. */ if (!uefi_check_ignore_db()) { db = get_cert_list(L"db", &secure_var, &dbsize, &status); @@ -102,20 +138,6 @@ static int __init load_uefi_certs(void) } } - mok = get_cert_list(L"MokListRT", &mok_var, &moksize, &status); - if (!mok) { - if (status == EFI_NOT_FOUND) - pr_debug("MokListRT variable wasn't found\n"); - else - pr_info("Couldn't get UEFI MokListRT\n"); - } else { - rc = parse_efi_signature_list("UEFI:MokListRT", - mok, moksize, get_handler_for_db); - if (rc) - pr_err("Couldn't parse MokListRT signatures: %d\n", rc); - kfree(mok); - } - dbx = get_cert_list(L"dbx", &secure_var, &dbxsize, &status); if (!dbx) { if (status == EFI_NOT_FOUND) @@ -131,6 +153,9 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* Load the MokListRT certs */ + rc = load_moklist_certs(); + return rc; } late_initcall(load_uefi_certs); From 726bd8965a5f112d9601f7ce68effa1e46e02bf2 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 4 Sep 2020 21:31:07 -0400 Subject: [PATCH 09/26] integrity: Load certs from the EFI MOK config table Because of system-specific EFI firmware limitations, EFI volatile variables may not be capable of holding the required contents of the Machine Owner Key (MOK) certificate store when the certificate list grows above some size. Therefore, an EFI boot loader may pass the MOK certs via a EFI configuration table created specifically for this purpose to avoid this firmware limitation. An EFI configuration table is a much more primitive mechanism compared to EFI variables and is well suited for one-way passage of static information from a pre-OS environment to the kernel. This patch adds the support to load certs from the MokListRT entry in the MOK variable configuration table, if it's present. The pre-existing support to load certs from the MokListRT EFI variable remains and is used if the EFI MOK configuration table isn't present or can't be successfully used. Signed-off-by: Lenny Szubowicz Link: https://lore.kernel.org/r/20200905013107.10457-4-lszubowi@redhat.com Signed-off-by: Ard Biesheuvel --- security/integrity/platform_certs/load_uefi.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index c1c622b4dc78..ee4b4c666854 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -71,16 +71,38 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, * Load the certs contained in the UEFI MokListRT database into the * platform trusted keyring. * + * This routine checks the EFI MOK config table first. If and only if + * that fails, this routine uses the MokListRT ordinary UEFI variable. + * * Return: Status */ static int __init load_moklist_certs(void) { + struct efi_mokvar_table_entry *mokvar_entry; efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; void *mok; unsigned long moksize; efi_status_t status; int rc; + /* First try to load certs from the EFI MOKvar config table. + * It's not an error if the MOKvar config table doesn't exist + * or the MokListRT entry is not found in it. + */ + mokvar_entry = efi_mokvar_entry_find("MokListRT"); + if (mokvar_entry) { + rc = parse_efi_signature_list("UEFI:MokListRT (MOKvar table)", + mokvar_entry->data, + mokvar_entry->data_size, + get_handler_for_db); + /* All done if that worked. */ + if (!rc) + return rc; + + pr_err("Couldn't parse MokListRT signatures from EFI MOKvar config table: %d\n", + rc); + } + /* Get MokListRT. It might not exist, so it isn't an error * if we can't get it. */ From c1df5e0c5796f775e60d1aec0b52f6d03d66ccd4 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 14 Sep 2020 17:35:34 -0400 Subject: [PATCH 10/26] efi/libstub: Add efi_warn and *_once logging helpers Add an efi_warn logging helper for warnings, and implement an analog of printk_once for once-only logging. Signed-off-by: Arvind Sankar Link: https://lore.kernel.org/r/20200914213535.933454-1-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 27cdcb11714d..9ea87a28d303 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -52,11 +52,34 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, #define efi_info(fmt, ...) \ efi_printk(KERN_INFO fmt, ##__VA_ARGS__) +#define efi_warn(fmt, ...) \ + efi_printk(KERN_WARNING "WARNING: " 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__) +#define efi_printk_once(fmt, ...) \ +({ \ + static bool __print_once; \ + bool __ret_print_once = !__print_once; \ + \ + if (!__print_once) { \ + __print_once = true; \ + efi_printk(fmt, ##__VA_ARGS__); \ + } \ + __ret_print_once; \ +}) + +#define efi_info_once(fmt, ...) \ + efi_printk_once(KERN_INFO fmt, ##__VA_ARGS__) +#define efi_warn_once(fmt, ...) \ + efi_printk_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__) +#define efi_err_once(fmt, ...) \ + efi_printk_once(KERN_ERR "ERROR: " fmt, ##__VA_ARGS__) +#define efi_debug_once(fmt, ...) \ + efi_printk_once(KERN_DEBUG "DEBUG: " fmt, ##__VA_ARGS__) + /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var #define fdt_setprop_inplace_var(fdt, node_offset, name, var) \ From 4a568ce29d3f48df95919f82a80e4b9be7ea0dc1 Mon Sep 17 00:00:00 2001 From: Arvind Sankar Date: Mon, 14 Sep 2020 17:35:35 -0400 Subject: [PATCH 11/26] efi/x86: Add a quirk to support command line arguments on Dell EFI firmware At least some versions of Dell EFI firmware pass the entire EFI_LOAD_OPTION descriptor, rather than just the OptionalData part, to the loaded image. This was verified with firmware revision 2.15.0 on a Dell Precision T3620 by Jacobo Pantoja. To handle this, add a quirk to check if the options look like a valid EFI_LOAD_OPTION descriptor, and if so, use the OptionalData part as the command line. Signed-off-by: Arvind Sankar Reported-by: Jacobo Pantoja Link: https://lore.kernel.org/linux-efi/20200907170021.GA2284449@rani.riverdale.lan/ Link: https://lore.kernel.org/r/20200914213535.933454-2-nivedita@alum.mit.edu Signed-off-by: Ard Biesheuvel --- .../firmware/efi/libstub/efi-stub-helper.c | 101 +++++++++++++++++- drivers/firmware/efi/libstub/efistub.h | 31 ++++++ drivers/firmware/efi/libstub/file.c | 5 +- 3 files changed, 135 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 6bca70bbb43d..4dbf04cc8930 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -230,6 +230,102 @@ efi_status_t efi_parse_options(char const *cmdline) return EFI_SUCCESS; } +/* + * The EFI_LOAD_OPTION descriptor has the following layout: + * u32 Attributes; + * u16 FilePathListLength; + * u16 Description[]; + * efi_device_path_protocol_t FilePathList[]; + * u8 OptionalData[]; + * + * This function validates and unpacks the variable-size data fields. + */ +static +bool efi_load_option_unpack(efi_load_option_unpacked_t *dest, + const efi_load_option_t *src, size_t size) +{ + const void *pos; + u16 c; + efi_device_path_protocol_t header; + const efi_char16_t *description; + const efi_device_path_protocol_t *file_path_list; + + if (size < offsetof(efi_load_option_t, variable_data)) + return false; + pos = src->variable_data; + size -= offsetof(efi_load_option_t, variable_data); + + if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0) + return false; + + /* Scan description. */ + description = pos; + do { + if (size < sizeof(c)) + return false; + c = *(const u16 *)pos; + pos += sizeof(c); + size -= sizeof(c); + } while (c != L'\0'); + + /* Scan file_path_list. */ + file_path_list = pos; + do { + if (size < sizeof(header)) + return false; + header = *(const efi_device_path_protocol_t *)pos; + if (header.length < sizeof(header)) + return false; + if (size < header.length) + return false; + pos += header.length; + size -= header.length; + } while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) || + (header.sub_type != EFI_DEV_END_ENTIRE)); + if (pos != (const void *)file_path_list + src->file_path_list_length) + return false; + + dest->attributes = src->attributes; + dest->file_path_list_length = src->file_path_list_length; + dest->description = description; + dest->file_path_list = file_path_list; + dest->optional_data_size = size; + dest->optional_data = size ? pos : NULL; + + return true; +} + +/* + * At least some versions of Dell firmware pass the entire contents of the + * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the + * OptionalData field. + * + * Detect this case and extract OptionalData. + */ +void efi_apply_loadoptions_quirk(const void **load_options, int *load_options_size) +{ + const efi_load_option_t *load_option = *load_options; + efi_load_option_unpacked_t load_option_unpacked; + + if (!IS_ENABLED(CONFIG_X86)) + return; + if (!load_option) + return; + if (*load_options_size < sizeof(*load_option)) + return; + if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) + return; + + if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size)) + return; + + efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); + efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); + + *load_options = load_option_unpacked.optional_data; + *load_options_size = load_option_unpacked.optional_data_size; +} + /* * Convert the unicode UEFI command line to ASCII to pass to kernel. * Size of memory allocated return in *cmd_line_len. @@ -239,12 +335,15 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) { const u16 *s2; unsigned long cmdline_addr = 0; - int options_chars = efi_table_attr(image, load_options_size) / 2; + int options_chars = efi_table_attr(image, load_options_size); const u16 *options = efi_table_attr(image, load_options); int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ bool in_quote = false; efi_status_t status; + efi_apply_loadoptions_quirk((const void **)&options, &options_chars); + options_chars /= sizeof(*options); + if (options) { s2 = options; while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 9ea87a28d303..2d7abcd99de9 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -708,6 +708,35 @@ union efi_load_file_protocol { } mixed_mode; }; +typedef struct { + u32 attributes; + u16 file_path_list_length; + u8 variable_data[]; + // efi_char16_t description[]; + // efi_device_path_protocol_t file_path_list[]; + // u8 optional_data[]; +} __packed efi_load_option_t; + +#define EFI_LOAD_OPTION_ACTIVE 0x0001U +#define EFI_LOAD_OPTION_FORCE_RECONNECT 0x0002U +#define EFI_LOAD_OPTION_HIDDEN 0x0008U +#define EFI_LOAD_OPTION_CATEGORY 0x1f00U +#define EFI_LOAD_OPTION_CATEGORY_BOOT 0x0000U +#define EFI_LOAD_OPTION_CATEGORY_APP 0x0100U + +#define EFI_LOAD_OPTION_BOOT_MASK \ + (EFI_LOAD_OPTION_ACTIVE|EFI_LOAD_OPTION_HIDDEN|EFI_LOAD_OPTION_CATEGORY) +#define EFI_LOAD_OPTION_MASK (EFI_LOAD_OPTION_FORCE_RECONNECT|EFI_LOAD_OPTION_BOOT_MASK) + +typedef struct { + u32 attributes; + u16 file_path_list_length; + const efi_char16_t *description; + const efi_device_path_protocol_t *file_path_list; + size_t optional_data_size; + const void *optional_data; +} efi_load_option_unpacked_t; + void efi_pci_disable_bridge_busmaster(void); typedef efi_status_t (*efi_exit_boot_map_processing)( @@ -750,6 +779,8 @@ __printf(1, 2) int efi_printk(char const *fmt, ...); void efi_free(unsigned long size, unsigned long addr); +void efi_apply_loadoptions_quirk(const void **load_options, int *load_options_size); + 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/file.c b/drivers/firmware/efi/libstub/file.c index 630caa6b1f4c..4e81c6077188 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -136,7 +136,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, unsigned long *load_size) { const efi_char16_t *cmdline = image->load_options; - int cmdline_len = image->load_options_size / 2; + int cmdline_len = image->load_options_size; unsigned long efi_chunk_size = ULONG_MAX; efi_file_protocol_t *volume = NULL; efi_file_protocol_t *file; @@ -148,6 +148,9 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, if (!load_addr || !load_size) return EFI_INVALID_PARAMETER; + efi_apply_loadoptions_quirk((const void **)&cmdline, &cmdline_len); + cmdline_len /= sizeof(*cmdline); + if (IS_ENABLED(CONFIG_X86) && !efi_nochunk) efi_chunk_size = EFI_READ_CHUNK_SIZE; From 9baf68cc4544056f33797b78ec09388f54ecc8f0 Mon Sep 17 00:00:00 2001 From: Alex Kluver Date: Wed, 19 Aug 2020 09:35:43 -0500 Subject: [PATCH 12/26] edac,ghes,cper: Add Row Extension to Memory Error Record Memory errors could be printed with incorrect row values since the DIMM size has outgrown the 16 bit row field in the CPER structure. UEFI Specification Version 2.8 has increased the size of row by allowing it to use the first 2 bits from a previously reserved space within the structure. When needed, add the extension bits to the row value printed. Based on UEFI 2.8 Table 299. Memory Error Record Signed-off-by: Alex Kluver Tested-by: Russ Anderson Reviewed-by: Steve Wahl Reviewed-by: Kyle Meyer Acked-by: Borislav Petkov Link: https://lore.kernel.org/r/20200819143544.155096-2-alex.kluver@hpe.com Signed-off-by: Ard Biesheuvel --- drivers/edac/ghes_edac.c | 8 ++++++-- drivers/firmware/efi/cper.c | 9 +++++++-- include/linux/cper.h | 16 ++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index da60c29468a7..741e7609e511 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -372,8 +372,12 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) p += sprintf(p, "rank:%d ", mem_err->rank); if (mem_err->validation_bits & CPER_MEM_VALID_BANK) p += sprintf(p, "bank:%d ", mem_err->bank); - if (mem_err->validation_bits & CPER_MEM_VALID_ROW) - p += sprintf(p, "row:%d ", mem_err->row); + if (mem_err->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { + u32 row = mem_err->row; + + row |= cper_get_mem_extension(mem_err->validation_bits, mem_err->extended); + p += sprintf(p, "row:%d ", row); + } if (mem_err->validation_bits & CPER_MEM_VALID_COLUMN) p += sprintf(p, "col:%d ", mem_err->column); if (mem_err->validation_bits & CPER_MEM_VALID_BIT_POSITION) diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index f564e15fbc7e..a60acd17bcaa 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -234,8 +234,12 @@ static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) n += scnprintf(msg + n, len - n, "device: %d ", mem->device); - if (mem->validation_bits & CPER_MEM_VALID_ROW) - n += scnprintf(msg + n, len - n, "row: %d ", mem->row); + if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { + u32 row = mem->row; + + row |= cper_get_mem_extension(mem->validation_bits, mem->extended); + n += scnprintf(msg + n, len - n, "row: %d ", row); + } if (mem->validation_bits & CPER_MEM_VALID_COLUMN) n += scnprintf(msg + n, len - n, "column: %d ", mem->column); if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) @@ -292,6 +296,7 @@ void cper_mem_err_pack(const struct cper_sec_mem_err *mem, cmem->requestor_id = mem->requestor_id; cmem->responder_id = mem->responder_id; cmem->target_id = mem->target_id; + cmem->extended = mem->extended; cmem->rank = mem->rank; cmem->mem_array_handle = mem->mem_array_handle; cmem->mem_dev_handle = mem->mem_dev_handle; diff --git a/include/linux/cper.h b/include/linux/cper.h index 8537e9282a65..bd2d8a77a784 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -230,6 +230,10 @@ enum { #define CPER_MEM_VALID_RANK_NUMBER 0x8000 #define CPER_MEM_VALID_CARD_HANDLE 0x10000 #define CPER_MEM_VALID_MODULE_HANDLE 0x20000 +#define CPER_MEM_VALID_ROW_EXT 0x40000 + +#define CPER_MEM_EXT_ROW_MASK 0x3 +#define CPER_MEM_EXT_ROW_SHIFT 16 #define CPER_PCIE_VALID_PORT_TYPE 0x0001 #define CPER_PCIE_VALID_VERSION 0x0002 @@ -443,7 +447,7 @@ struct cper_sec_mem_err_old { u8 error_type; }; -/* Memory Error Section (UEFI >= v2.3), UEFI v2.7 sec N.2.5 */ +/* Memory Error Section (UEFI >= v2.3), UEFI v2.8 sec N.2.5 */ struct cper_sec_mem_err { u64 validation_bits; u64 error_status; @@ -461,7 +465,7 @@ struct cper_sec_mem_err { u64 responder_id; u64 target_id; u8 error_type; - u8 reserved; + u8 extended; u16 rank; u16 mem_array_handle; /* "card handle" in UEFI 2.4 */ u16 mem_dev_handle; /* "module handle" in UEFI 2.4 */ @@ -483,8 +487,16 @@ struct cper_mem_err_compact { u16 rank; u16 mem_array_handle; u16 mem_dev_handle; + u8 extended; }; +static inline u32 cper_get_mem_extension(u64 mem_valid, u8 mem_extended) +{ + if (!(mem_valid & CPER_MEM_VALID_ROW_EXT)) + return 0; + return (mem_extended & CPER_MEM_EXT_ROW_MASK) << CPER_MEM_EXT_ROW_SHIFT; +} + /* PCI Express Error Section, UEFI v2.7 sec N.2.7 */ struct cper_sec_pcie { u64 validation_bits; From 612b5d506d066cdf0a739963e7cd28642d500ec1 Mon Sep 17 00:00:00 2001 From: Alex Kluver Date: Wed, 19 Aug 2020 09:35:44 -0500 Subject: [PATCH 13/26] cper,edac,efi: Memory Error Record: bank group/address and chip id Updates to the UEFI 2.8 Memory Error Record allow splitting the bank field into bank address and bank group, and using the last 3 bits of the extended field as a chip identifier. When needed, print correct version of bank field, bank group, and chip identification. Based on UEFI 2.8 Table 299. Memory Error Record. Signed-off-by: Alex Kluver Reviewed-by: Russ Anderson Reviewed-by: Kyle Meyer Reviewed-by: Steve Wahl Acked-by: Borislav Petkov Link: https://lore.kernel.org/r/20200819143544.155096-3-alex.kluver@hpe.com Signed-off-by: Ard Biesheuvel --- drivers/edac/ghes_edac.c | 9 +++++++++ drivers/firmware/efi/cper.c | 9 +++++++++ include/linux/cper.h | 8 ++++++++ 3 files changed, 26 insertions(+) diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 741e7609e511..8a44f32db849 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -372,6 +372,12 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) p += sprintf(p, "rank:%d ", mem_err->rank); if (mem_err->validation_bits & CPER_MEM_VALID_BANK) p += sprintf(p, "bank:%d ", mem_err->bank); + if (mem_err->validation_bits & CPER_MEM_VALID_BANK_GROUP) + p += sprintf(p, "bank_group:%d ", + mem_err->bank >> CPER_MEM_BANK_GROUP_SHIFT); + if (mem_err->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) + p += sprintf(p, "bank_address:%d ", + mem_err->bank & CPER_MEM_BANK_ADDRESS_MASK); if (mem_err->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { u32 row = mem_err->row; @@ -399,6 +405,9 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) strcpy(e->label, dimm->label); } } + if (mem_err->validation_bits & CPER_MEM_VALID_CHIP_ID) + p += sprintf(p, "chipID: %d ", + mem_err->extended >> CPER_MEM_CHIP_ID_SHIFT); if (p > e->location) *(p - 1) = '\0'; diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index a60acd17bcaa..e15d484b6a5a 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -232,6 +232,12 @@ static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); if (mem->validation_bits & CPER_MEM_VALID_BANK) n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); + if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) + n += scnprintf(msg + n, len - n, "bank_group: %d ", + mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); + if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) + n += scnprintf(msg + n, len - n, "bank_address: %d ", + mem->bank & CPER_MEM_BANK_ADDRESS_MASK); if (mem->validation_bits & CPER_MEM_VALID_DEVICE) n += scnprintf(msg + n, len - n, "device: %d ", mem->device); if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { @@ -254,6 +260,9 @@ static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) scnprintf(msg + n, len - n, "target_id: 0x%016llx ", mem->target_id); + if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) + scnprintf(msg + n, len - n, "chip_id: %d ", + mem->extended >> CPER_MEM_CHIP_ID_SHIFT); msg[n] = '\0'; return n; diff --git a/include/linux/cper.h b/include/linux/cper.h index bd2d8a77a784..6a511a1078ca 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -231,10 +231,18 @@ enum { #define CPER_MEM_VALID_CARD_HANDLE 0x10000 #define CPER_MEM_VALID_MODULE_HANDLE 0x20000 #define CPER_MEM_VALID_ROW_EXT 0x40000 +#define CPER_MEM_VALID_BANK_GROUP 0x80000 +#define CPER_MEM_VALID_BANK_ADDRESS 0x100000 +#define CPER_MEM_VALID_CHIP_ID 0x200000 #define CPER_MEM_EXT_ROW_MASK 0x3 #define CPER_MEM_EXT_ROW_SHIFT 16 +#define CPER_MEM_BANK_ADDRESS_MASK 0xff +#define CPER_MEM_BANK_GROUP_SHIFT 8 + +#define CPER_MEM_CHIP_ID_SHIFT 5 + #define CPER_PCIE_VALID_PORT_TYPE 0x0001 #define CPER_PCIE_VALID_VERSION 0x0002 #define CPER_PCIE_VALID_COMMAND_STATUS 0x0004 From 6277e374b0b07c1a93c829f0a27e38739b3b7a1b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 24 Sep 2020 13:52:24 +0200 Subject: [PATCH 14/26] efi: Add definition of EFI_MEMORY_CPU_CRYPTO and ability to report it Incorporate the definition of EFI_MEMORY_CPU_CRYPTO from the UEFI specification v2.8, and wire it into our memory map dumping routine as well. To make a bit of space in the output buffer, which is provided by the various callers, shorten the descriptive names of the memory types. Reviewed-by: Laszlo Ersek Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 47 +++++++++++++++++++------------------- include/linux/efi.h | 1 + 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 3aa07c3b5136..ebb59e52294f 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -714,7 +714,7 @@ void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, vendor); } -static __initdata char memory_type_name[][20] = { +static __initdata char memory_type_name[][13] = { "Reserved", "Loader Code", "Loader Data", @@ -722,14 +722,14 @@ static __initdata char memory_type_name[][20] = { "Boot Data", "Runtime Code", "Runtime Data", - "Conventional Memory", - "Unusable Memory", - "ACPI Reclaim Memory", - "ACPI Memory NVS", - "Memory Mapped I/O", - "MMIO Port Space", + "Conventional", + "Unusable", + "ACPI Reclaim", + "ACPI Mem NVS", + "MMIO", + "MMIO Port", "PAL Code", - "Persistent Memory", + "Persistent", }; char * __init efi_md_typeattr_format(char *buf, size_t size, @@ -756,26 +756,27 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | - EFI_MEMORY_NV | EFI_MEMORY_SP | + EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_CPU_CRYPTO | EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) snprintf(pos, size, "|attr=0x%016llx]", (unsigned long long)attr); else snprintf(pos, size, - "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", - attr & EFI_MEMORY_RUNTIME ? "RUN" : "", - attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", - attr & EFI_MEMORY_SP ? "SP" : "", - attr & EFI_MEMORY_NV ? "NV" : "", - attr & EFI_MEMORY_XP ? "XP" : "", - attr & EFI_MEMORY_RP ? "RP" : "", - attr & EFI_MEMORY_WP ? "WP" : "", - attr & EFI_MEMORY_RO ? "RO" : "", - attr & EFI_MEMORY_UCE ? "UCE" : "", - attr & EFI_MEMORY_WB ? "WB" : "", - attr & EFI_MEMORY_WT ? "WT" : "", - attr & EFI_MEMORY_WC ? "WC" : "", - attr & EFI_MEMORY_UC ? "UC" : ""); + "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + attr & EFI_MEMORY_RUNTIME ? "RUN" : "", + attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", + attr & EFI_MEMORY_CPU_CRYPTO ? "CC" : "", + attr & EFI_MEMORY_SP ? "SP" : "", + attr & EFI_MEMORY_NV ? "NV" : "", + attr & EFI_MEMORY_XP ? "XP" : "", + attr & EFI_MEMORY_RP ? "RP" : "", + attr & EFI_MEMORY_WP ? "WP" : "", + attr & EFI_MEMORY_RO ? "RO" : "", + attr & EFI_MEMORY_UCE ? "UCE" : "", + attr & EFI_MEMORY_WB ? "WB" : "", + attr & EFI_MEMORY_WT ? "WT" : "", + attr & EFI_MEMORY_WC ? "WC" : "", + attr & EFI_MEMORY_UC ? "UC" : ""); return buf; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 73db1ae04cef..f216c029a77b 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -122,6 +122,7 @@ typedef struct { ((u64)0x0000000000010000ULL) /* higher reliability */ #define EFI_MEMORY_RO ((u64)0x0000000000020000ULL) /* read-only */ #define EFI_MEMORY_SP ((u64)0x0000000000040000ULL) /* soft reserved */ +#define EFI_MEMORY_CPU_CRYPTO ((u64)0x0000000000080000ULL) /* supports encryption */ #define EFI_MEMORY_RUNTIME ((u64)0x8000000000000000ULL) /* range requires runtime mapping */ #define EFI_MEMORY_DESCRIPTOR_VERSION 1 From aad0f3d693bbb356b9478879ecd245d4f7a2beb0 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 21 Sep 2020 09:53:23 +0800 Subject: [PATCH 15/26] efi/libstub: Fix missing-prototypes in string.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following warnings. drivers/firmware/efi/libstub/string.c:83:20: warning: no previous prototype for ‘simple_strtoull’ [-Wmissing-prototypes] drivers/firmware/efi/libstub/string.c:108:6: warning: no previous prototype for ‘simple_strtol’ [-Wmissing-prototypes] Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1600653203-57909-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/string.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 1ac2f8764715..5d13e43869ee 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -7,6 +7,7 @@ */ #include +#include #include #include From f5344e5d6ccb9ddf377202690a135bc64607c621 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Thu, 24 Sep 2020 10:20:18 +0800 Subject: [PATCH 16/26] efi: Delete deprecated parameter comments Delete deprecated parameter comments to fix warnings reported by make W=1. drivers/firmware/efi/vars.c:428: warning: Excess function parameter 'atomic' description in 'efivar_init' Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1600914018-12697-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/vars.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 973eef234b36..274b0eea0607 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -414,7 +414,6 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, * efivar_init - build the initial list of EFI variables * @func: callback function to invoke for every variable * @data: function-specific data to pass to @func - * @atomic: do we need to execute the @func-loop atomically? * @duplicates: error if we encounter duplicates on @head? * @head: initialised head of variable list * From 336af6a4686d885a067ecea8c3c3dd129ba4fc75 Mon Sep 17 00:00:00 2001 From: Michael Schaller Date: Fri, 25 Sep 2020 09:45:02 +0200 Subject: [PATCH 17/26] efivarfs: Replace invalid slashes with exclamation marks in dentries. Without this patch efivarfs_alloc_dentry creates dentries with slashes in their name if the respective EFI variable has slashes in its name. This in turn causes EIO on getdents64, which prevents a complete directory listing of /sys/firmware/efi/efivars/. This patch replaces the invalid shlashes with exclamation marks like kobject_set_name_vargs does for /sys/firmware/efi/vars/ to have consistently named dentries under /sys/firmware/efi/vars/ and /sys/firmware/efi/efivars/. Signed-off-by: Michael Schaller Link: https://lore.kernel.org/r/20200925074502.150448-1-misch@google.com Signed-off-by: Ard Biesheuvel --- fs/efivarfs/super.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 28bb5689333a..15880a68faad 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -141,6 +141,9 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; + /* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */ + strreplace(name, '/', '!'); + inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0, is_removable); if (!inode) From d32de9130f6c79533508e2c7879f18997bfbe2a0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 26 Sep 2020 10:52:42 +0200 Subject: [PATCH 18/26] efi/arm64: libstub: Deal gracefully with EFI_RNG_PROTOCOL failure Currently, on arm64, we abort on any failure from efi_get_random_bytes() other than EFI_NOT_FOUND when it comes to setting the physical seed for KASLR, but ignore such failures when obtaining the seed for virtual KASLR or for early seeding of the kernel's entropy pool via the config table. This is inconsistent, and may lead to unexpected boot failures. So let's permit any failure for the physical seed, and simply report the error code if it does not equal EFI_NOT_FOUND. Cc: # v5.8+ Reported-by: Heinrich Schuchardt Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm64-stub.c | 8 +++++--- drivers/firmware/efi/libstub/fdt.c | 4 +--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index e5bfac79e5ac..04f5d79d4265 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -62,10 +62,12 @@ 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) { - efi_info("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n"); + efi_info("EFI_RNG_PROTOCOL unavailable, KASLR will be disabled\n"); + efi_nokaslr = true; } else if (status != EFI_SUCCESS) { - efi_err("efi_get_random_bytes() failed\n"); - return status; + efi_err("efi_get_random_bytes() failed (0x%lx), KASLR will be disabled\n", + status); + efi_nokaslr = true; } } else { efi_info("KASLR disabled on kernel command line\n"); diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 11ecf3c4640e..368cd60000ee 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -136,7 +136,7 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, if (status) goto fdt_set_fail; - if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) { efi_status_t efi_status; efi_status = efi_get_random_bytes(sizeof(fdt_val64), @@ -145,8 +145,6 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size, status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64); if (status) goto fdt_set_fail; - } else if (efi_status != EFI_NOT_FOUND) { - return efi_status; } } From b89114cd018cffa5deb7def1844ce1891efd4f96 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 24 Sep 2020 17:58:22 +0200 Subject: [PATCH 19/26] efi: mokvar-table: fix some issues in new code Fix a couple of issues in the new mokvar-table handling code, as pointed out by Arvind and Boris: - don't bother checking the end of the physical region against the start address of the mokvar table, - ensure that we enter the loop with err = -EINVAL, - replace size_t with unsigned long to appease pedantic type equality checks. Reviewed-by: Arvind Sankar Reviewed-by: Lenny Szubowicz Tested-by: Borislav Petkov Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/mokvar-table.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c index b1cd49893d4d..72a9e1736fef 100644 --- a/drivers/firmware/efi/mokvar-table.c +++ b/drivers/firmware/efi/mokvar-table.c @@ -98,15 +98,14 @@ static struct kobject *mokvar_kobj; void __init efi_mokvar_table_init(void) { efi_memory_desc_t md; - u64 end_pa; void *va = NULL; - size_t cur_offset = 0; - size_t offset_limit; - size_t map_size = 0; - size_t map_size_needed = 0; - size_t size; + unsigned long cur_offset = 0; + unsigned long offset_limit; + unsigned long map_size = 0; + unsigned long map_size_needed = 0; + unsigned long size; struct efi_mokvar_table_entry *mokvar_entry; - int err = -EINVAL; + int err; if (!efi_enabled(EFI_MEMMAP)) return; @@ -122,18 +121,16 @@ void __init efi_mokvar_table_init(void) pr_warn("EFI MOKvar config table is not within the EFI memory map\n"); return; } - end_pa = efi_mem_desc_end(&md); - if (efi.mokvar_table >= end_pa) { - pr_err("EFI memory descriptor containing MOKvar config table is invalid\n"); - return; - } - offset_limit = end_pa - efi.mokvar_table; + + offset_limit = efi_mem_desc_end(&md) - efi.mokvar_table; + /* * Validate the MOK config table. Since there is no table header * from which we could get the total size of the MOK config table, * we compute the total size as we validate each variably sized * entry, remapping as necessary. */ + err = -EINVAL; while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) { mokvar_entry = va + cur_offset; map_size_needed = cur_offset + sizeof(*mokvar_entry); @@ -150,7 +147,7 @@ void __init efi_mokvar_table_init(void) offset_limit); va = early_memremap(efi.mokvar_table, map_size); if (!va) { - pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%zu.\n", + pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n", efi.mokvar_table, map_size); return; } From 232f4eb6393f42f7f9418560ae9228e747fc6faf Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 09:56:14 +0200 Subject: [PATCH 20/26] efi: pstore: disentangle from deprecated efivars module The EFI pstore implementation relies on the 'efivars' abstraction, which encapsulates the EFI variable store in a way that can be overridden by other backing stores, like the Google SMI one. On top of that, the EFI pstore implementation also relies on the efivars.ko module, which is a separate layer built on top of the 'efivars' abstraction that exposes the [deprecated] sysfs entries for each variable that exists in the backing store. Since the efivars.ko module is deprecated, and all users appear to have moved to the efivarfs file system instead, let's prepare for its removal, by removing EFI pstore's dependency on it. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 2 +- drivers/firmware/efi/efi-pstore.c | 76 +++++++++++++++++++++++++++++-- drivers/firmware/efi/efivars.c | 41 +---------------- include/linux/efi.h | 4 -- 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 3939699e62fe..dd8d10817bdf 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -26,7 +26,7 @@ config EFI_ESRT config EFI_VARS_PSTORE tristate "Register efivars backend for pstore" - depends on EFI_VARS && PSTORE + depends on PSTORE default y help Say Y here to enable use efivars as a backend to pstore. This diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index feb7fe6f2da7..785f5e6b3a41 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -8,6 +8,8 @@ #define DUMP_NAME_LEN 66 +#define EFIVARS_DATA_SIZE_MAX 1024 + static bool efivars_pstore_disable = IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); @@ -18,6 +20,8 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); EFI_VARIABLE_BOOTSERVICE_ACCESS | \ EFI_VARIABLE_RUNTIME_ACCESS) +static LIST_HEAD(efi_pstore_list); + static int efi_pstore_open(struct pstore_info *psi) { psi->data = NULL; @@ -126,7 +130,7 @@ static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry, if (entry->deleting) { list_del(&entry->list); efivar_entry_iter_end(); - efivar_unregister(entry); + kfree(entry); if (efivar_entry_iter_begin()) return -EINTR; } else if (turn_off_scanning) @@ -169,7 +173,7 @@ static int efi_pstore_sysfs_entry_iter(struct pstore_record *record) { struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data; struct efivar_entry *entry, *n; - struct list_head *head = &efivar_sysfs_list; + struct list_head *head = &efi_pstore_list; int size = 0; int ret; @@ -314,12 +318,12 @@ static int efi_pstore_erase_name(const char *name) if (efivar_entry_iter_begin()) return -EINTR; - found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, + found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list, efi_name, &entry); efivar_entry_iter_end(); if (found && !entry->scanning) - efivar_unregister(entry); + kfree(entry); return found ? 0 : -ENOENT; } @@ -354,14 +358,76 @@ static struct pstore_info efi_pstore_info = { .erase = efi_pstore_erase, }; +static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor, + unsigned long name_size, void *data) +{ + struct efivar_entry *entry; + int ret; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + memcpy(entry->var.VariableName, name, name_size); + entry->var.VendorGuid = vendor; + + ret = efivar_entry_add(entry, &efi_pstore_list); + if (ret) + kfree(entry); + + return ret; +} + +static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor, + unsigned long name_size, void *data) +{ + struct efivar_entry *entry = data; + + if (efivar_entry_find(name, vendor, &efi_pstore_list, false)) + return 0; + + memcpy(entry->var.VariableName, name, name_size); + memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); + + return 1; +} + +static void efi_pstore_update_entries(struct work_struct *work) +{ + struct efivar_entry *entry; + int err; + + /* Add new sysfs entries */ + while (1) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + err = efivar_init(efi_pstore_update_entry, entry, + false, &efi_pstore_list); + if (!err) + break; + + efivar_entry_add(entry, &efi_pstore_list); + } + + kfree(entry); +} + static __init int efivars_pstore_init(void) { + int ret; + if (!efivars_kobject() || !efivar_supports_writes()) return 0; if (efivars_pstore_disable) return 0; + ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list); + if (ret) + return ret; + efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); if (!efi_pstore_info.buf) return -ENOMEM; @@ -374,6 +440,8 @@ static __init int efivars_pstore_init(void) efi_pstore_info.bufsize = 0; } + INIT_WORK(&efivar_work, efi_pstore_update_entries); + return 0; } diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index dcea137142b3..f39321dbc29f 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -24,8 +24,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(EFIVARS_VERSION); MODULE_ALIAS("platform:efivars"); -LIST_HEAD(efivar_sysfs_list); -EXPORT_SYMBOL_GPL(efivar_sysfs_list); +static LIST_HEAD(efivar_sysfs_list); static struct kset *efivars_kset; @@ -591,42 +590,6 @@ out_free: return error; } -static int efivar_update_sysfs_entry(efi_char16_t *name, efi_guid_t vendor, - unsigned long name_size, void *data) -{ - struct efivar_entry *entry = data; - - if (efivar_entry_find(name, vendor, &efivar_sysfs_list, false)) - return 0; - - memcpy(entry->var.VariableName, name, name_size); - memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); - - return 1; -} - -static void efivar_update_sysfs_entries(struct work_struct *work) -{ - struct efivar_entry *entry; - int err; - - /* Add new sysfs entries */ - while (1) { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return; - - err = efivar_init(efivar_update_sysfs_entry, entry, - false, &efivar_sysfs_list); - if (!err) - break; - - efivar_create_sysfs_entry(entry); - } - - kfree(entry); -} - static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, unsigned long name_size, void *data) { @@ -701,8 +664,6 @@ int efivars_sysfs_init(void) return error; } - INIT_WORK(&efivar_work, efivar_update_sysfs_entries); - return 0; } EXPORT_SYMBOL_GPL(efivars_sysfs_init); diff --git a/include/linux/efi.h b/include/linux/efi.h index 4a2332f146eb..7066c11ab82f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -986,8 +986,6 @@ struct efivar_entry { bool deleting; }; -extern struct list_head efivar_sysfs_list; - static inline void efivar_unregister(struct efivar_entry *var) { @@ -1045,8 +1043,6 @@ void efivar_run_worker(void); #if defined(CONFIG_EFI_VARS) || defined(CONFIG_EFI_VARS_MODULE) int efivars_sysfs_init(void); -#define EFIVARS_DATA_SIZE_MAX 1024 - #endif /* CONFIG_EFI_VARS */ extern bool efi_capsule_pending(int *reset_type); From c9b51a2dbfe7f47643e133bf48e1bf28f1b85d2a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 10:07:49 +0200 Subject: [PATCH 21/26] efi: pstore: move workqueue handling out of efivars The worker thread that gets kicked off to sync the state of the EFI variable list is only used by the EFI pstore implementation, and is defined in its source file. So let's move its scheduling there as well. Since our efivar_init() scan will bail on duplicate entries, there is no need to disable the workqueue like we did before, so we can run it unconditionally. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-pstore.c | 7 +++++-- drivers/firmware/efi/vars.c | 21 --------------------- include/linux/efi.h | 3 --- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 785f5e6b3a41..0ef086e43090 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -21,6 +21,7 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); EFI_VARIABLE_RUNTIME_ACCESS) static LIST_HEAD(efi_pstore_list); +static DECLARE_WORK(efivar_work, NULL); static int efi_pstore_open(struct pstore_info *psi) { @@ -267,8 +268,9 @@ static int efi_pstore_write(struct pstore_record *record) ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES, preemptible(), record->size, record->psi->buf); - if (record->reason == KMSG_DUMP_OOPS) - efivar_run_worker(); + if (record->reason == KMSG_DUMP_OOPS && try_module_get(THIS_MODULE)) + if (!schedule_work(&efivar_work)) + module_put(THIS_MODULE); return ret; }; @@ -412,6 +414,7 @@ static void efi_pstore_update_entries(struct work_struct *work) } kfree(entry); + module_put(THIS_MODULE); } static __init int efivars_pstore_init(void) diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 973eef234b36..ffb12f6efc97 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -32,10 +32,6 @@ static struct efivars *__efivars; */ static DEFINE_SEMAPHORE(efivars_lock); -static bool efivar_wq_enabled = true; -DECLARE_WORK(efivar_work, NULL); -EXPORT_SYMBOL_GPL(efivar_work); - static bool validate_device_path(efi_char16_t *var_name, int match, u8 *buffer, unsigned long len) @@ -391,13 +387,6 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, size_t i, len8 = len16 / sizeof(efi_char16_t); char *str8; - /* - * Disable the workqueue since the algorithm it uses for - * detecting new variables won't work with this buggy - * implementation of GetNextVariableName(). - */ - efivar_wq_enabled = false; - str8 = kzalloc(len8, GFP_KERNEL); if (!str8) return; @@ -1157,16 +1146,6 @@ struct kobject *efivars_kobject(void) } EXPORT_SYMBOL_GPL(efivars_kobject); -/** - * efivar_run_worker - schedule the efivar worker thread - */ -void efivar_run_worker(void) -{ - if (efivar_wq_enabled) - schedule_work(&efivar_work); -} -EXPORT_SYMBOL_GPL(efivar_run_worker); - /** * efivars_register - register an efivars * @efivars: efivars to register diff --git a/include/linux/efi.h b/include/linux/efi.h index 7066c11ab82f..ab8c80331217 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1037,9 +1037,6 @@ bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, bool efivar_variable_is_removable(efi_guid_t vendor, const char *name, size_t len); -extern struct work_struct efivar_work; -void efivar_run_worker(void); - #if defined(CONFIG_EFI_VARS) || defined(CONFIG_EFI_VARS_MODULE) int efivars_sysfs_init(void); From 5d3c8617ccee6387ba73a5dba77fb9dc21cb85f4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 10:13:07 +0200 Subject: [PATCH 22/26] efi: efivars: un-export efivars_sysfs_init() efivars_sysfs_init() is only used locally in the source file that defines it, so make it static and unexport it. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efivars.c | 3 +-- include/linux/efi.h | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index f39321dbc29f..a76f50e15e6d 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -638,7 +638,7 @@ static void efivars_sysfs_exit(void) kset_unregister(efivars_kset); } -int efivars_sysfs_init(void) +static int efivars_sysfs_init(void) { struct kobject *parent_kobj = efivars_kobject(); int error = 0; @@ -666,7 +666,6 @@ int efivars_sysfs_init(void) return 0; } -EXPORT_SYMBOL_GPL(efivars_sysfs_init); module_init(efivars_sysfs_init); module_exit(efivars_sysfs_exit); diff --git a/include/linux/efi.h b/include/linux/efi.h index ab8c80331217..4c8dae0b5c75 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1037,10 +1037,6 @@ bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, bool efivar_variable_is_removable(efi_guid_t vendor, const char *name, size_t len); -#if defined(CONFIG_EFI_VARS) || defined(CONFIG_EFI_VARS_MODULE) -int efivars_sysfs_init(void); - -#endif /* CONFIG_EFI_VARS */ extern bool efi_capsule_pending(int *reset_type); extern int efi_capsule_supported(efi_guid_t guid, u32 flags, From 9846d86031eeca2fb2867fe4ac9d92803a97e8e4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 10:18:31 +0200 Subject: [PATCH 23/26] efi: gsmi: fix false dependency on CONFIG_EFI_VARS The gsmi code does not actually rely on CONFIG_EFI_VARS, since it only uses the efivars abstraction that is included unconditionally when CONFIG_EFI is defined. CONFIG_EFI_VARS controls the inclusion of the code that exposes the sysfs entries, and which has been deprecated for some time. Signed-off-by: Ard Biesheuvel --- drivers/firmware/google/Kconfig | 2 +- drivers/firmware/google/gsmi.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index a3a6ca659ffa..97968aece54f 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -15,7 +15,7 @@ config GOOGLE_SMI help Say Y here if you want to enable SMI callbacks for Google platforms. This provides an interface for writing to and - clearing the event log. If EFI_VARS is also enabled this + clearing the event log. If CONFIG_EFI is also enabled this driver provides an interface for reading and writing NVRAM variables. diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 5b2011ebbe26..7d9367b22010 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -302,7 +302,7 @@ static int gsmi_exec(u8 func, u8 sub) return rc; } -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI static struct efivars efivars; @@ -483,7 +483,7 @@ static const struct efivar_operations efivar_ops = { .get_next_variable = gsmi_get_next_variable, }; -#endif /* CONFIG_EFI_VARS */ +#endif /* CONFIG_EFI */ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, @@ -1007,7 +1007,7 @@ static __init int gsmi_init(void) goto out_remove_bin_file; } -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); if (ret) { printk(KERN_INFO "gsmi: Failed to register efivars\n"); @@ -1047,7 +1047,7 @@ static void __exit gsmi_exit(void) unregister_die_notifier(&gsmi_die_notifier); atomic_notifier_chain_unregister(&panic_notifier_list, &gsmi_panic_notifier); -#ifdef CONFIG_EFI_VARS +#ifdef CONFIG_EFI efivars_unregister(&efivars); #endif From 5ee70cd60652e85e4e8ced99f58f2fcbab405110 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 10:27:36 +0200 Subject: [PATCH 24/26] efi: remove some false dependencies on CONFIG_EFI_VARS Remove some false dependencies on CONFIG_EFI_VARS, which only controls the creation of the sysfs entries, whereas the underlying functionality that these modules rely on is enabled unconditionally when CONFIG_EFI is set. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index dd8d10817bdf..80f5c67e3638 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -137,7 +137,6 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" - depends on EFI_VARS default n help This module installs a reboot hook, such that if reboot() is @@ -281,7 +280,7 @@ config EFI_EARLYCON config EFI_CUSTOM_SSDT_OVERLAYS bool "Load custom ACPI SSDT overlay from an EFI variable" - depends on EFI_VARS && ACPI + depends on EFI && ACPI default ACPI_TABLE_UPGRADE help Allow loading of an ACPI SSDT overlay from an EFI variable specified From 963fabf37f6a94214a823df0a785e653cb8ad6ea Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 23 Sep 2020 10:20:10 +0200 Subject: [PATCH 25/26] efi: efivars: limit availability to X86 builds CONFIG_EFI_VARS controls the code that exposes EFI variables via sysfs entries, which was deprecated before support for non-Intel architectures was added to EFI. So let's limit its availability to Intel architectures for the time being, and hopefully remove it entirely in the not too distant future. While at it, let's remove the module alias so that the module is no longer loaded automatically. Signed-off-by: Ard Biesheuvel --- Documentation/arm/uefi.rst | 2 +- drivers/firmware/efi/Kconfig | 13 ++++--------- drivers/firmware/efi/efivars.c | 1 - 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Documentation/arm/uefi.rst b/Documentation/arm/uefi.rst index f868330df6be..f732f957421f 100644 --- a/Documentation/arm/uefi.rst +++ b/Documentation/arm/uefi.rst @@ -23,7 +23,7 @@ makes it possible for the kernel to support additional features: For actually enabling [U]EFI support, enable: - CONFIG_EFI=y -- CONFIG_EFI_VARS=y or m +- CONFIG_EFIVAR_FS=y or m The implementation depends on receiving information about the UEFI environment in a Flattened Device Tree (FDT) - so is only available with CONFIG_OF. diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 80f5c67e3638..da1887f72a51 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -4,20 +4,15 @@ menu "EFI (Extensible Firmware Interface) Support" config EFI_VARS tristate "EFI Variable Support via sysfs" - depends on EFI + depends on EFI && (X86 || IA64) default n help If you say Y here, you are able to get EFI (Extensible Firmware Interface) variable information via sysfs. You may read, write, create, and destroy EFI variables through this interface. - - Note that using this driver in concert with efibootmgr requires - at least test release version 0.5.0-test3 or later, which is - available from: - - - Subsequent efibootmgr releases may be found at: - + Note that this driver is only retained for compatibility with + legacy users: new users should use the efivarfs filesystem + instead. config EFI_ESRT bool diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index a76f50e15e6d..e6b16b3a17a8 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -22,7 +22,6 @@ MODULE_AUTHOR("Matt Domsch "); MODULE_DESCRIPTION("sysfs interface to EFI Variables"); MODULE_LICENSE("GPL"); MODULE_VERSION(EFIVARS_VERSION); -MODULE_ALIAS("platform:efivars"); static LIST_HEAD(efivar_sysfs_list); From cc383a9e245c527d3175e2cf4cced9dbbedbbac6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 2 Oct 2020 10:01:23 +0200 Subject: [PATCH 26/26] efi: mokvar: add missing include of asm/early_ioremap.h Nathan reports that building the new mokvar table code for 32-bit ARM fails with errors such as error: implicit declaration of function 'early_memunmap' error: implicit declaration of function 'early_memremap' This is caused by the lack of an explicit #include of the appropriate header, and ARM apparently does not inherit that inclusion via another header file. So add the #include. Tested-by: Nathan Chancellor Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/mokvar-table.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c index 72a9e1736fef..d8bc01340686 100644 --- a/drivers/firmware/efi/mokvar-table.c +++ b/drivers/firmware/efi/mokvar-table.c @@ -40,6 +40,8 @@ #include #include +#include + /* * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed * sequence of struct efi_mokvar_table_entry, one for each named