From 085e2ff9aeb03b591946fda422c25d6933f8948d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 10:21:20 +0200 Subject: efi: libstub: Drop randomization of runtime memory map Randomizing the UEFI runtime memory map requires the use of the SetVirtualAddressMap() EFI boot service, which we prefer to avoid. So let's drop randomization, which was already problematic in combination with hibernation, which means that distro kernels never enabled it in the first place. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index cf474f0dd261..0ebfa2d9628a 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -35,15 +35,6 @@ * as well to minimize the code churn. */ #define EFI_RT_VIRTUAL_BASE SZ_512M -#define EFI_RT_VIRTUAL_SIZE SZ_512M - -#ifdef CONFIG_ARM64 -# define EFI_RT_VIRTUAL_LIMIT DEFAULT_MAP_WINDOW_64 -#elif defined(CONFIG_RISCV) || defined(CONFIG_LOONGARCH) -# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE_MIN -#else /* Only if TASK_SIZE is a constant */ -# define EFI_RT_VIRTUAL_LIMIT TASK_SIZE -#endif /* * Some architectures map the EFI regions into the kernel's linear map using a @@ -230,26 +221,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_novamap |= !(get_supported_rt_services() & EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP); - /* hibernation expects the runtime regions to stay in the same place */ - if (!IS_ENABLED(CONFIG_HIBERNATION) && !efi_nokaslr && !flat_va_mapping) { - /* - * Randomize the base of the UEFI runtime services region. - * Preserve the 2 MB alignment of the region by taking a - * shift of 21 bit positions into account when scaling - * the headroom value using a 32-bit random value. - */ - static const u64 headroom = EFI_RT_VIRTUAL_LIMIT - - EFI_RT_VIRTUAL_BASE - - EFI_RT_VIRTUAL_SIZE; - u32 rnd; - - status = efi_get_random_bytes(sizeof(rnd), (u8 *)&rnd); - if (status == EFI_SUCCESS) { - virtmap_base = EFI_RT_VIRTUAL_BASE + - (((headroom >> 21) * rnd) >> (32 - 21)); - } - } - install_memreserve_table(); status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr); -- cgit v1.2.3 From 48b9491cfe0816c7afc4602db1536cc6b2938acd Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 10:25:36 +0200 Subject: efi: libstub: Drop handling of EFI properties table The EFI properties table was a short lived experiment that never saw the light of day on non-x86 (if at all) so let's drop the handling of it. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 0ebfa2d9628a..b55d1009d4c8 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -126,7 +126,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, unsigned long reserve_addr = 0; unsigned long reserve_size = 0; struct screen_info *si; - efi_properties_table_t *prop_tbl; efi_system_table = sys_table_arg; @@ -205,18 +204,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_random_get_seed(); - /* - * If the NX PE data feature is enabled in the properties table, we - * should take care not to create a virtual mapping that changes the - * relative placement of runtime services code and data regions, as - * they may belong to the same PE/COFF executable image in memory. - * The easiest way to achieve that is to simply use a 1:1 mapping. - */ - prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID); - flat_va_mapping |= prop_tbl && - (prop_tbl->memory_protection_attribute & - EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); - /* force efi_novamap if SetVirtualAddressMap() is unsupported */ efi_novamap |= !(get_supported_rt_services() & EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP); -- cgit v1.2.3 From 0d60ffeec53cc378cc282d51e870275c642532c6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Oct 2022 09:53:40 +0200 Subject: efi: libstub: Deduplicate ftrace command line argument filtering No need for the same pattern to be used four times for each architecture individually if we can just apply it once later. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index b1601aad7e1a..26b0a421a286 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -5,6 +5,10 @@ # things like ftrace and stack-protector are likely to cause trouble if left # enabled, even if doing so doesn't break the build. # + +# non-x86 reuses KBUILD_CFLAGS, x86 does not +cflags-y := $(KBUILD_CFLAGS) + cflags-$(CONFIG_X86_32) := -march=i386 cflags-$(CONFIG_X86_64) := -mcmodel=small cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ @@ -18,20 +22,17 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpie $(DISABLE_STACKLEAK_PLUGIN) \ +cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ $(call cc-option,-mbranch-protection=none) -cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fno-builtin -fpic \ +cflags-$(CONFIG_ARM) += -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) -cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpic -cflags-$(CONFIG_LOONGARCH) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpie +cflags-$(CONFIG_RISCV) += -fpic +cflags-$(CONFIG_LOONGARCH) += -fpie cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt -KBUILD_CFLAGS := $(cflags-y) -Os -DDISABLE_BRANCH_PROFILING \ +KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \ + -Os -DDISABLE_BRANCH_PROFILING \ -include $(srctree)/include/linux/hidden.h \ -D__NO_FORTIFY \ -ffreestanding \ -- cgit v1.2.3 From aaeb3fc614d65ec5c0b838ed1afb59c3f0f04643 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Oct 2022 16:06:39 +0200 Subject: arm64: efi: Move dcache cleaning of loaded image out of efi_enter_kernel() The efi_enter_kernel() routine will be shared between the existing EFI stub and the zboot decompressor, and the version of dcache_clean_to_poc() that the core kernel exports to the stub will not be available in the latter case. So move the handling into the .c file which will remain part of the stub build that integrates directly with the kernel proper. Signed-off-by: Ard Biesheuvel Acked-by: Catalin Marinas --- arch/arm64/kernel/efi-entry.S | 9 --------- arch/arm64/kernel/image-vars.h | 1 - drivers/firmware/efi/libstub/arm64-stub.c | 10 +++++++++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S index 61a87fa1c305..1c1be004a271 100644 --- a/arch/arm64/kernel/efi-entry.S +++ b/arch/arm64/kernel/efi-entry.S @@ -23,15 +23,6 @@ SYM_CODE_START(efi_enter_kernel) add x19, x0, x2 // relocated Image entrypoint mov x20, x1 // DTB address - /* - * Clean the copied Image to the PoC, and ensure it is not shadowed by - * stale icache entries from before relocation. - */ - ldr w1, =kernel_size - add x1, x0, x1 - bl dcache_clean_poc - ic ialluis - /* * Clean the remainder of this routine to the PoC * so that we can safely disable the MMU and caches. diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 8151412653de..74d20835cf91 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -10,7 +10,6 @@ #error This file should only be included in vmlinux.lds.S #endif -PROVIDE(__efistub_kernel_size = _edata - _text); PROVIDE(__efistub_primary_entry_offset = primary_entry - _text); /* diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 259e4b852d63..bcb21afd8aeb 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -156,7 +156,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, */ *image_addr = (u64)_text; *reserve_size = 0; - return EFI_SUCCESS; + goto clean_image_to_poc; } status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, @@ -172,5 +172,13 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, *image_addr = *reserve_addr; memcpy((void *)*image_addr, _text, kernel_size); +clean_image_to_poc: + /* + * Clean the copied Image to the PoC, and ensure it is not shadowed by + * stale icache entries from before relocation. + */ + dcache_clean_poc(*image_addr, *image_addr + kernel_size); + asm("ic ialluis"); + return EFI_SUCCESS; } -- cgit v1.2.3 From 7a35cb0a6ec0c4fbb3ea23526d56c59c8cdf779b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Oct 2022 16:28:22 +0200 Subject: arm64: efi: Avoid dcache_clean_poc() altogether in efi_enter_kernel() To allow efi_enter_kernel() to be shared with the EFI zboot decompressor build, drop another reference to dcache_clean_poc() and replace it with a single DC CVAC* instruction. To ensure that it covers the remainder of efi_enter_kernel() as intended, reorganize the code a bit so it fits in a 32-byte cacheline, and align it to 32 bytes. (Even though the architecture defines 16 as the minimum D-cache line size, even the chosen value of 32 is highly unlikely to ever be encountered on real hardware, and this works with any line size >= 32) * due to ARM64_WORKAROUND_CLEAN_CACHE, we actually use a DC CIVAC here Signed-off-by: Ard Biesheuvel Acked-by: Catalin Marinas --- arch/arm64/kernel/efi-entry.S | 57 ++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S index 1c1be004a271..8bce13356e29 100644 --- a/arch/arm64/kernel/efi-entry.S +++ b/arch/arm64/kernel/efi-entry.S @@ -21,40 +21,41 @@ SYM_CODE_START(efi_enter_kernel) */ ldr w2, =primary_entry_offset add x19, x0, x2 // relocated Image entrypoint - mov x20, x1 // DTB address + + mov x0, x1 // DTB address + mov x1, xzr + mov x2, xzr + mov x3, xzr /* * Clean the remainder of this routine to the PoC * so that we can safely disable the MMU and caches. */ - adr x0, 0f - adr x1, 3f - bl dcache_clean_poc -0: + adr x4, 1f + dc civac, x4 + dsb sy + /* Turn off Dcache and MMU */ - mrs x0, CurrentEL - cmp x0, #CurrentEL_EL2 - b.ne 1f - mrs x0, sctlr_el2 - bic x0, x0, #1 << 0 // clear SCTLR.M - bic x0, x0, #1 << 2 // clear SCTLR.C - pre_disable_mmu_workaround - msr sctlr_el2, x0 - isb + mrs x4, CurrentEL + cmp x4, #CurrentEL_EL2 + mrs x4, sctlr_el1 + b.ne 0f + mrs x4, sctlr_el2 +0: bic x4, x4, #SCTLR_ELx_M + bic x4, x4, #SCTLR_ELx_C + b.eq 1f b 2f -1: - mrs x0, sctlr_el1 - bic x0, x0, #1 << 0 // clear SCTLR.M - bic x0, x0, #1 << 2 // clear SCTLR.C - pre_disable_mmu_workaround - msr sctlr_el1, x0 + + .balign 32 +1: pre_disable_mmu_workaround + msr sctlr_el2, x4 isb -2: - /* Jump to kernel entry point */ - mov x0, x20 - mov x1, xzr - mov x2, xzr - mov x3, xzr - br x19 -3: + br x19 // jump to kernel entrypoint + +2: pre_disable_mmu_workaround + msr sctlr_el1, x4 + isb + br x19 // jump to kernel entrypoint + + .org 1b + 32 SYM_CODE_END(efi_enter_kernel) -- cgit v1.2.3 From 4ef806096bdbbe9e77c29c17c3ed636d00d6d6f3 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 17 Oct 2022 17:14:41 +0200 Subject: arm64: efi: Move efi-entry.S into the libstub source directory We will be sharing efi-entry.S with the zboot decompressor build, which does not link against vmlinux directly. So move it into the libstub source directory so we can include in the libstub static library. Signed-off-by: Ard Biesheuvel Acked-by: Catalin Marinas --- arch/arm64/kernel/Makefile | 9 +---- arch/arm64/kernel/efi-entry.S | 61 ------------------------------ drivers/firmware/efi/libstub/Makefile | 4 +- drivers/firmware/efi/libstub/arm64-entry.S | 57 ++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 71 deletions(-) delete mode 100644 arch/arm64/kernel/efi-entry.S create mode 100644 drivers/firmware/efi/libstub/arm64-entry.S diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 2f361a883d8c..2a3b0e4950f2 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -36,12 +36,6 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ syscall.o proton-pack.o idreg-override.o idle.o \ patching.o -targets += efi-entry.o - -OBJCOPYFLAGS := --prefix-symbols=__efistub_ -$(obj)/%.stub.o: $(obj)/%.o FORCE - $(call if_changed,objcopy) - obj-$(CONFIG_COMPAT) += sys32.o signal32.o \ sys_compat.o obj-$(CONFIG_COMPAT) += sigreturn32.o @@ -57,8 +51,7 @@ obj-$(CONFIG_CPU_PM) += sleep.o suspend.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KGDB) += kgdb.o -obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o \ - efi-rt-wrapper.o +obj-$(CONFIG_EFI) += efi.o efi-rt-wrapper.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o obj-$(CONFIG_ACPI) += acpi.o diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S deleted file mode 100644 index 8bce13356e29..000000000000 --- a/arch/arm64/kernel/efi-entry.S +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * EFI entry point. - * - * Copyright (C) 2013, 2014 Red Hat, Inc. - * Author: Mark Salter - */ -#include -#include - -#include - - __INIT - -SYM_CODE_START(efi_enter_kernel) - /* - * efi_pe_entry() will have copied the kernel image if necessary and we - * end up here with device tree address in x1 and the kernel entry - * point stored in x0. Save those values in registers which are - * callee preserved. - */ - ldr w2, =primary_entry_offset - add x19, x0, x2 // relocated Image entrypoint - - mov x0, x1 // DTB address - mov x1, xzr - mov x2, xzr - mov x3, xzr - - /* - * Clean the remainder of this routine to the PoC - * so that we can safely disable the MMU and caches. - */ - adr x4, 1f - dc civac, x4 - dsb sy - - /* Turn off Dcache and MMU */ - mrs x4, CurrentEL - cmp x4, #CurrentEL_EL2 - mrs x4, sctlr_el1 - b.ne 0f - mrs x4, sctlr_el2 -0: bic x4, x4, #SCTLR_ELx_M - bic x4, x4, #SCTLR_ELx_C - b.eq 1f - b 2f - - .balign 32 -1: pre_disable_mmu_workaround - msr sctlr_el2, x4 - isb - br x19 // jump to kernel entrypoint - -2: pre_disable_mmu_workaround - msr sctlr_el1, x4 - isb - br x19 // jump to kernel entrypoint - - .org 1b + 32 -SYM_CODE_END(efi_enter_kernel) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 26b0a421a286..01a0be468a66 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -83,7 +83,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o lib-$(CONFIG_ARM) += arm32-stub.o -lib-$(CONFIG_ARM64) += arm64-stub.o +lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o lib-$(CONFIG_X86) += x86-stub.o lib-$(CONFIG_RISCV) += riscv-stub.o lib-$(CONFIG_LOONGARCH) += loongarch-stub.o @@ -137,7 +137,7 @@ STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS # STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ -STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS +STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS64 # For RISC-V, we don't need anything special other than arm64. Keep all the # symbols in .init section and make sure that no absolute symbols references diff --git a/drivers/firmware/efi/libstub/arm64-entry.S b/drivers/firmware/efi/libstub/arm64-entry.S new file mode 100644 index 000000000000..87082b222a87 --- /dev/null +++ b/drivers/firmware/efi/libstub/arm64-entry.S @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * EFI entry point. + * + * Copyright (C) 2013, 2014 Red Hat, Inc. + * Author: Mark Salter + */ +#include +#include + +SYM_CODE_START(efi_enter_kernel) + /* + * efi_pe_entry() will have copied the kernel image if necessary and we + * end up here with device tree address in x1 and the kernel entry + * point stored in x0. Save those values in registers which are + * callee preserved. + */ + ldr w2, =primary_entry_offset + add x19, x0, x2 // relocated Image entrypoint + + mov x0, x1 // DTB address + mov x1, xzr + mov x2, xzr + mov x3, xzr + + /* + * Clean the remainder of this routine to the PoC + * so that we can safely disable the MMU and caches. + */ + adr x4, 1f + dc civac, x4 + dsb sy + + /* Turn off Dcache and MMU */ + mrs x4, CurrentEL + cmp x4, #CurrentEL_EL2 + mrs x4, sctlr_el1 + b.ne 0f + mrs x4, sctlr_el2 +0: bic x4, x4, #SCTLR_ELx_M + bic x4, x4, #SCTLR_ELx_C + b.eq 1f + b 2f + + .balign 32 +1: pre_disable_mmu_workaround + msr sctlr_el2, x4 + isb + br x19 // jump to kernel entrypoint + +2: pre_disable_mmu_workaround + msr sctlr_el1, x4 + isb + br x19 // jump to kernel entrypoint + + .org 1b + 32 +SYM_CODE_END(efi_enter_kernel) -- cgit v1.2.3 From f9a3c8488762284c855942d917200d2846392e40 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Fri, 28 Oct 2022 13:56:19 +0200 Subject: MAINTAINERS: adjust entry after arm64 efi-entry.S file movement Commit 6ed27461c9c8 ("arm64: efi: Move efi-entry.S into the libstub source directory") moves arch/arm64/kernel/efi-entry.S to drivers/firmware/efi/libstub/arm64-entry.S, but misses to adjust the MAINTAINERS file. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken file pattern. Adjust the file entry in EXTENSIBLE FIRMWARE INTERFACE (EFI) to this file movement. As the file drivers/firmware/efi/libstub/arm64-entry.S is already covered by the entry drivers/firmware/efi/, simply remove the arm64 file entry. Signed-off-by: Lukas Bulwahn Signed-off-by: Ard Biesheuvel --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 046ff06ff97f..6d49b9554046 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7789,7 +7789,6 @@ F: Documentation/admin-guide/efi-stub.rst F: arch/*/include/asm/efi.h F: arch/*/kernel/efi.c F: arch/arm/boot/compressed/efi-header.S -F: arch/arm64/kernel/efi-entry.S F: arch/x86/platform/efi/ F: drivers/firmware/efi/ F: include/linux/efi*.h -- cgit v1.2.3 From fa882a1389b2a6eaba8a0d5439dbd32537d0ecc5 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 15:41:08 +0200 Subject: efi: libstub: Use local strncmp() implementation unconditionally In preparation for moving the EFI stub functionality into the zboot decompressor, switch to the stub's implementation of strncmp() unconditionally. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/image-vars.h | 1 - arch/riscv/kernel/image-vars.h | 1 - drivers/firmware/efi/libstub/string.c | 2 -- 3 files changed, 4 deletions(-) diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 74d20835cf91..5a12ca2d7158 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -26,7 +26,6 @@ PROVIDE(__efistub_memchr = __pi_memchr); PROVIDE(__efistub_strlen = __pi_strlen); PROVIDE(__efistub_strnlen = __pi_strnlen); PROVIDE(__efistub_strcmp = __pi_strcmp); -PROVIDE(__efistub_strncmp = __pi_strncmp); PROVIDE(__efistub_strrchr = __pi_strrchr); PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc); diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index d6e5f739905e..b46322cb5864 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -28,7 +28,6 @@ __efistub_memchr = memchr; __efistub_strlen = strlen; __efistub_strnlen = strnlen; __efistub_strcmp = strcmp; -__efistub_strncmp = strncmp; __efistub_strrchr = strrchr; __efistub__start = _start; diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 5d13e43869ee..9f5810d86646 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -35,7 +35,6 @@ char *strstr(const char *s1, const char *s2) } #endif -#ifndef __HAVE_ARCH_STRNCMP /** * strncmp - Compare two length-limited strings * @cs: One string @@ -57,7 +56,6 @@ int strncmp(const char *cs, const char *ct, size_t count) } return 0; } -#endif /* Works only for digits and letters, but small and fast */ #define TOLOWER(x) ((x) | 0x20) -- cgit v1.2.3 From 52dce39cd2786673969a12d1c94e0922d9208f83 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 15:15:51 +0200 Subject: efi: libstub: Clone memcmp() into the stub We will no longer be able to call into the kernel image once we merge the decompressor with the EFI stub, so we need our own implementation of memcmp(). Let's add the one from lib/string.c and simplify it. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/image-vars.h | 1 - arch/loongarch/kernel/image-vars.h | 1 - arch/riscv/kernel/image-vars.h | 1 - drivers/firmware/efi/libstub/intrinsics.c | 18 ++++++++++++++++++ drivers/firmware/efi/libstub/zboot.c | 11 +---------- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 5a12ca2d7158..f7c8cdf0c302 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -21,7 +21,6 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text); * linked at. The routines below are all implemented in assembler in a * position independent manner */ -PROVIDE(__efistub_memcmp = __pi_memcmp); PROVIDE(__efistub_memchr = __pi_memchr); PROVIDE(__efistub_strlen = __pi_strlen); PROVIDE(__efistub_strnlen = __pi_strnlen); diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h index 88f5d81702df..5b6c7f079942 100644 --- a/arch/loongarch/kernel/image-vars.h +++ b/arch/loongarch/kernel/image-vars.h @@ -7,7 +7,6 @@ #ifdef CONFIG_EFI_STUB -__efistub_memcmp = memcmp; __efistub_memchr = memchr; __efistub_strcat = strcat; __efistub_strcmp = strcmp; diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index b46322cb5864..9229cfe0754d 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -23,7 +23,6 @@ * linked at. The routines below are all implemented in assembler in a * position independent manner */ -__efistub_memcmp = memcmp; __efistub_memchr = memchr; __efistub_strlen = strlen; __efistub_strnlen = strnlen; diff --git a/drivers/firmware/efi/libstub/intrinsics.c b/drivers/firmware/efi/libstub/intrinsics.c index a04ab39292b6..965e734f6f98 100644 --- a/drivers/firmware/efi/libstub/intrinsics.c +++ b/drivers/firmware/efi/libstub/intrinsics.c @@ -28,3 +28,21 @@ void *memset(void *dst, int c, size_t len) efi_bs_call(set_mem, dst, len, c & U8_MAX); return dst; } + +/** + * memcmp - Compare two areas of memory + * @cs: One area of memory + * @ct: Another area of memory + * @count: The size of the area. + */ +#undef memcmp +int memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index ea72c8f27da6..38173ec29cd5 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -47,15 +47,6 @@ static void error(char *x) log(L"error() called from decompressor library\n"); } -// Local version to avoid pulling in memcmp() -static bool guids_eq(const efi_guid_t *a, const efi_guid_t *b) -{ - const u32 *l = (u32 *)a; - const u32 *r = (u32 *)b; - - return l[0] == r[0] && l[1] == r[1] && l[2] == r[2] && l[3] == r[3]; -} - static efi_status_t __efiapi load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem, bool boot_policy, unsigned long *bufsize, void *buffer) @@ -76,7 +67,7 @@ load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem, if (rem->type == EFI_DEV_MEDIA && rem->sub_type == EFI_DEV_MEDIA_VENDOR) { vendor_dp = container_of(rem, struct efi_vendor_dev_path, header); - if (!guids_eq(&vendor_dp->vendorguid, &LINUX_EFI_ZBOOT_MEDIA_GUID)) + if (efi_guidcmp(vendor_dp->vendorguid, LINUX_EFI_ZBOOT_MEDIA_GUID)) return EFI_NOT_FOUND; decompress = true; -- cgit v1.2.3 From 2e6fa86f2d484ff9a0047e20a48c1400314a5bb6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 15:27:45 +0200 Subject: efi: libstub: Enable efi_printk() in zboot decompressor Split the efi_printk() routine into its own source file, and provide local implementations of strlen() and strnlen() so that the standalone zboot app can efi_err and efi_info etc. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/image-vars.h | 2 - arch/loongarch/kernel/image-vars.h | 2 - arch/riscv/kernel/image-vars.h | 2 - drivers/firmware/efi/libstub/Makefile | 5 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 143 ----------------------- drivers/firmware/efi/libstub/printk.c | 154 +++++++++++++++++++++++++ drivers/firmware/efi/libstub/string.c | 33 +++++- drivers/firmware/efi/libstub/zboot.c | 30 ++--- 8 files changed, 198 insertions(+), 173 deletions(-) create mode 100644 drivers/firmware/efi/libstub/printk.c diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index f7c8cdf0c302..ace584e0b065 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -22,8 +22,6 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text); * position independent manner */ PROVIDE(__efistub_memchr = __pi_memchr); -PROVIDE(__efistub_strlen = __pi_strlen); -PROVIDE(__efistub_strnlen = __pi_strnlen); PROVIDE(__efistub_strcmp = __pi_strcmp); PROVIDE(__efistub_strrchr = __pi_strrchr); PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc); diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h index 5b6c7f079942..28bc632ec296 100644 --- a/arch/loongarch/kernel/image-vars.h +++ b/arch/loongarch/kernel/image-vars.h @@ -10,10 +10,8 @@ __efistub_memchr = memchr; __efistub_strcat = strcat; __efistub_strcmp = strcmp; -__efistub_strlen = strlen; __efistub_strncat = strncat; __efistub_strnstr = strnstr; -__efistub_strnlen = strnlen; __efistub_strrchr = strrchr; __efistub_kernel_entry = kernel_entry; __efistub_kernel_asize = kernel_asize; diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 9229cfe0754d..4913abd6dd10 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -24,8 +24,6 @@ * position independent manner */ __efistub_memchr = memchr; -__efistub_strlen = strlen; -__efistub_strnlen = strnlen; __efistub_strcmp = strcmp; __efistub_strrchr = strrchr; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 01a0be468a66..5b0ae755180e 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -24,7 +24,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ # disable the stackleak plugin cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ $(call cc-option,-mbranch-protection=none) -cflags-$(CONFIG_ARM) += -fno-builtin -fpic \ +cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ + -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) cflags-$(CONFIG_RISCV) += -fpic cflags-$(CONFIG_LOONGARCH) += -fpie @@ -68,7 +69,7 @@ KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ file.o mem.o random.o randomalloc.o pci.o \ skip_spaces.o lib-cmdline.o lib-ctype.o \ - alignedmem.o relocate.o vsprintf.o + alignedmem.o relocate.o printk.o vsprintf.o # include the stub's libfdt dependencies from lib/ when needed libfdt-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \ diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 0c493521b25b..2184f3f153ef 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -9,10 +9,8 @@ #include -#include #include #include -#include /* For CONSOLE_LOGLEVEL_* */ #include #include @@ -20,7 +18,6 @@ bool efi_nochunk; bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); -int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; bool efi_novamap; static bool efi_noinitrd; @@ -32,146 +29,6 @@ bool __pure __efi_soft_reserve_enabled(void) return !efi_nosoftreserve; } -/** - * efi_char16_puts() - Write a UCS-2 encoded string to the console - * @str: UCS-2 encoded string - */ -void efi_char16_puts(efi_char16_t *str) -{ - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, str); -} - -static -u32 utf8_to_utf32(const u8 **s8) -{ - u32 c32; - u8 c0, cx; - size_t clen, i; - - c0 = cx = *(*s8)++; - /* - * The position of the most-significant 0 bit gives us the length of - * a multi-octet encoding. - */ - for (clen = 0; cx & 0x80; ++clen) - cx <<= 1; - /* - * If the 0 bit is in position 8, this is a valid single-octet - * encoding. If the 0 bit is in position 7 or positions 1-3, the - * encoding is invalid. - * In either case, we just return the first octet. - */ - if (clen < 2 || clen > 4) - return c0; - /* Get the bits from the first octet. */ - c32 = cx >> clen--; - for (i = 0; i < clen; ++i) { - /* Trailing octets must have 10 in most significant bits. */ - cx = (*s8)[i] ^ 0x80; - if (cx & 0xc0) - return c0; - c32 = (c32 << 6) | cx; - } - /* - * Check for validity: - * - The character must be in the Unicode range. - * - It must not be a surrogate. - * - It must be encoded using the correct number of octets. - */ - if (c32 > 0x10ffff || - (c32 & 0xf800) == 0xd800 || - clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) - return c0; - *s8 += clen; - return c32; -} - -/** - * efi_puts() - Write a UTF-8 encoded string to the console - * @str: UTF-8 encoded string - */ -void efi_puts(const char *str) -{ - efi_char16_t buf[128]; - size_t pos = 0, lim = ARRAY_SIZE(buf); - const u8 *s8 = (const u8 *)str; - u32 c32; - - while (*s8) { - if (*s8 == '\n') - buf[pos++] = L'\r'; - c32 = utf8_to_utf32(&s8); - if (c32 < 0x10000) { - /* Characters in plane 0 use a single word. */ - buf[pos++] = c32; - } else { - /* - * Characters in other planes encode into a surrogate - * pair. - */ - buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); - buf[pos++] = 0xdc00 + (c32 & 0x3ff); - } - if (*s8 == '\0' || pos >= lim - 2) { - buf[pos] = L'\0'; - efi_char16_puts(buf); - pos = 0; - } - } -} - -/** - * efi_printk() - Print a kernel message - * @fmt: format string - * - * The first letter of the format string is used to determine the logging level - * of the message. If the level is less then the current EFI logging level, the - * message is suppressed. The message will be truncated to 255 bytes. - * - * Return: number of printed characters - */ -int efi_printk(const char *fmt, ...) -{ - char printf_buf[256]; - va_list args; - int printed; - int loglevel = printk_get_level(fmt); - - switch (loglevel) { - case '0' ... '9': - loglevel -= '0'; - break; - default: - /* - * Use loglevel -1 for cases where we just want to print to - * the screen. - */ - loglevel = -1; - break; - } - - if (loglevel >= efi_loglevel) - return 0; - - if (loglevel >= 0) - efi_puts("EFI stub: "); - - fmt = printk_skip_level(fmt); - - va_start(args, fmt); - printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); - va_end(args); - - efi_puts(printf_buf); - if (printed >= sizeof(printf_buf)) { - efi_puts("[Message truncated]\n"); - return -1; - } - - return printed; -} - /** * efi_parse_options() - Parse EFI command line options * @cmdline: kernel command line diff --git a/drivers/firmware/efi/libstub/printk.c b/drivers/firmware/efi/libstub/printk.c new file mode 100644 index 000000000000..3a67a2cea7bd --- /dev/null +++ b/drivers/firmware/efi/libstub/printk.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include +#include +#include +#include /* For CONSOLE_LOGLEVEL_* */ +#include +#include + +#include "efistub.h" + +int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; + +/** + * efi_char16_puts() - Write a UCS-2 encoded string to the console + * @str: UCS-2 encoded string + */ +void efi_char16_puts(efi_char16_t *str) +{ + efi_call_proto(efi_table_attr(efi_system_table, con_out), + output_string, str); +} + +static +u32 utf8_to_utf32(const u8 **s8) +{ + u32 c32; + u8 c0, cx; + size_t clen, i; + + c0 = cx = *(*s8)++; + /* + * The position of the most-significant 0 bit gives us the length of + * a multi-octet encoding. + */ + for (clen = 0; cx & 0x80; ++clen) + cx <<= 1; + /* + * If the 0 bit is in position 8, this is a valid single-octet + * encoding. If the 0 bit is in position 7 or positions 1-3, the + * encoding is invalid. + * In either case, we just return the first octet. + */ + if (clen < 2 || clen > 4) + return c0; + /* Get the bits from the first octet. */ + c32 = cx >> clen--; + for (i = 0; i < clen; ++i) { + /* Trailing octets must have 10 in most significant bits. */ + cx = (*s8)[i] ^ 0x80; + if (cx & 0xc0) + return c0; + c32 = (c32 << 6) | cx; + } + /* + * Check for validity: + * - The character must be in the Unicode range. + * - It must not be a surrogate. + * - It must be encoded using the correct number of octets. + */ + if (c32 > 0x10ffff || + (c32 & 0xf800) == 0xd800 || + clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) + return c0; + *s8 += clen; + return c32; +} + +/** + * efi_puts() - Write a UTF-8 encoded string to the console + * @str: UTF-8 encoded string + */ +void efi_puts(const char *str) +{ + efi_char16_t buf[128]; + size_t pos = 0, lim = ARRAY_SIZE(buf); + const u8 *s8 = (const u8 *)str; + u32 c32; + + while (*s8) { + if (*s8 == '\n') + buf[pos++] = L'\r'; + c32 = utf8_to_utf32(&s8); + if (c32 < 0x10000) { + /* Characters in plane 0 use a single word. */ + buf[pos++] = c32; + } else { + /* + * Characters in other planes encode into a surrogate + * pair. + */ + buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); + buf[pos++] = 0xdc00 + (c32 & 0x3ff); + } + if (*s8 == '\0' || pos >= lim - 2) { + buf[pos] = L'\0'; + efi_char16_puts(buf); + pos = 0; + } + } +} + +/** + * efi_printk() - Print a kernel message + * @fmt: format string + * + * The first letter of the format string is used to determine the logging level + * of the message. If the level is less then the current EFI logging level, the + * message is suppressed. The message will be truncated to 255 bytes. + * + * Return: number of printed characters + */ +int efi_printk(const char *fmt, ...) +{ + char printf_buf[256]; + va_list args; + int printed; + int loglevel = printk_get_level(fmt); + + switch (loglevel) { + case '0' ... '9': + loglevel -= '0'; + break; + default: + /* + * Use loglevel -1 for cases where we just want to print to + * the screen. + */ + loglevel = -1; + break; + } + + if (loglevel >= efi_loglevel) + return 0; + + if (loglevel >= 0) + efi_puts("EFI stub: "); + + fmt = printk_skip_level(fmt); + + va_start(args, fmt); + printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); + va_end(args); + + efi_puts(printf_buf); + if (printed >= sizeof(printf_buf)) { + efi_puts("[Message truncated]\n"); + return -1; + } + + return printed; +} diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 9f5810d86646..5a1519d435bf 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -11,7 +11,37 @@ #include #include -#ifndef __HAVE_ARCH_STRSTR +#ifndef EFI_HAVE_STRLEN +/** + * strlen - Find the length of a string + * @s: The string to be sized + */ +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +#ifndef EFI_HAVE_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + /** * strstr - Find the first substring in a %NUL terminated string * @s1: The string to be searched @@ -33,7 +63,6 @@ char *strstr(const char *s1, const char *s2) } return NULL; } -#endif /** * strncmp - Compare two length-limited strings diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 38173ec29cd5..5f41a5b17d6e 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -32,19 +32,9 @@ static unsigned long free_mem_ptr, free_mem_end_ptr; extern char efi_zboot_header[]; extern char _gzdata_start[], _gzdata_end[]; -static void log(efi_char16_t str[]) -{ - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, L"EFI decompressor: "); - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, str); - efi_call_proto(efi_table_attr(efi_system_table, con_out), - output_string, L"\n"); -} - static void error(char *x) { - log(L"error() called from decompressor library\n"); + efi_err("EFI decompressor: %s\n", x); } static efi_status_t __efiapi @@ -91,7 +81,7 @@ load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem, ret = __decompress(_gzdata_start, compressed_size, NULL, NULL, buffer, size, NULL, error); if (ret < 0) { - log(L"Decompression failed"); + error("Decompression failed"); return EFI_DEVICE_ERROR; } } else { @@ -180,7 +170,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) status = efi_bs_call(handle_protocol, handle, &LOADED_IMAGE_PROTOCOL_GUID, (void **)&parent); if (status != EFI_SUCCESS) { - log(L"Failed to locate parent's loaded image protocol"); + error("Failed to locate parent's loaded image protocol"); return status; } @@ -207,7 +197,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) sizeof(struct efi_vendor_dev_path), (void **)&dp_alloc); if (status != EFI_SUCCESS) { - log(L"Failed to allocate device path pool memory"); + error("Failed to allocate device path pool memory"); return status; } @@ -236,21 +226,21 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) &EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2, NULL); if (status != EFI_SUCCESS) { - log(L"Failed to install LoadFile2 protocol and device path"); + error("Failed to install LoadFile2 protocol and device path"); goto free_dpalloc; } status = efi_bs_call(load_image, false, handle, li_dp, NULL, 0, &child_handle); if (status != EFI_SUCCESS) { - log(L"Failed to load image"); + error("Failed to load image"); goto uninstall_lf2; } status = efi_bs_call(handle_protocol, child_handle, &LOADED_IMAGE_PROTOCOL_GUID, (void **)&child); if (status != EFI_SUCCESS) { - log(L"Failed to locate child's loaded image protocol"); + error("Failed to locate child's loaded image protocol"); goto unload_image; } @@ -261,9 +251,9 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) status = efi_bs_call(start_image, child_handle, &exit_data_size, &exit_data); if (status != EFI_SUCCESS) { - log(L"StartImage() returned with error"); + error("StartImage() returned with error:"); if (exit_data_size > 0) - log(exit_data); + efi_err("%ls\n", exit_data); // If StartImage() returns EFI_SECURITY_VIOLATION, the image is // not unloaded so we need to do it by hand. @@ -286,7 +276,7 @@ free_dpalloc: // Free ExitData in case Exit() returned with a failure code, // but return the original status code. - log(L"Exit() returned with failure code"); + error("Exit() returned with failure code"); if (exit_data != NULL) efi_bs_call(free_pool, exit_data); return status; -- cgit v1.2.3 From 06064800d92a1adac3717e86cc6ba5f98c0ef40e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 18:24:42 +0200 Subject: efi: loongarch: Drop exports of unused string routines Drop the __efistub_ prefixed exports of various routines that the EFI stub on LoongArch does not even use. Signed-off-by: Ard Biesheuvel --- arch/loongarch/kernel/image-vars.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h index 28bc632ec296..e561989d02de 100644 --- a/arch/loongarch/kernel/image-vars.h +++ b/arch/loongarch/kernel/image-vars.h @@ -7,12 +7,7 @@ #ifdef CONFIG_EFI_STUB -__efistub_memchr = memchr; -__efistub_strcat = strcat; __efistub_strcmp = strcmp; -__efistub_strncat = strncat; -__efistub_strnstr = strnstr; -__efistub_strrchr = strrchr; __efistub_kernel_entry = kernel_entry; __efistub_kernel_asize = kernel_asize; __efistub_kernel_fsize = kernel_fsize; -- cgit v1.2.3 From 732ea9db9d8a6a0444d18ed810cccb2428d8766b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 17:10:39 +0200 Subject: efi: libstub: Move screen_info handling to common code Currently, arm64, RISC-V and LoongArch rely on the fact that struct screen_info can be accessed directly, due to the fact that the EFI stub and the core kernel are part of the same image. This will change after a future patch, so let's ensure that the screen_info handling is able to deal with this, by adopting the arm32 approach of passing it as a configuration table. While at it, switch to ACPI reclaim memory to hold the screen_info data, which is more appropriate for this kind of allocation. Signed-off-by: Ard Biesheuvel --- arch/arm/include/asm/efi.h | 3 -- arch/arm/kernel/efi.c | 31 +++-------------- arch/arm64/include/asm/efi.h | 6 ---- arch/loongarch/include/asm/efi.h | 9 ----- arch/loongarch/kernel/efi.c | 24 +++++++++++-- arch/riscv/include/asm/efi.h | 6 ---- drivers/firmware/efi/efi-init.c | 21 +++++++++-- drivers/firmware/efi/efi.c | 5 +++ drivers/firmware/efi/libstub/Makefile | 3 +- drivers/firmware/efi/libstub/arm32-stub.c | 37 -------------------- drivers/firmware/efi/libstub/efi-stub.c | 9 +++++ drivers/firmware/efi/libstub/efistub.h | 3 ++ drivers/firmware/efi/libstub/screen_info.c | 56 ++++++++++++++++++++++++++++++ include/linux/efi.h | 2 +- 14 files changed, 121 insertions(+), 94 deletions(-) create mode 100644 drivers/firmware/efi/libstub/screen_info.c diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h index 4bdd930167c0..b95241b1ca65 100644 --- a/arch/arm/include/asm/efi.h +++ b/arch/arm/include/asm/efi.h @@ -43,9 +43,6 @@ void efi_virtmap_unload(void); /* arch specific definitions used by the stub code */ -struct screen_info *alloc_screen_info(void); -void free_screen_info(struct screen_info *si); - /* * A reasonable upper bound for the uncompressed kernel size is 32 MBytes, * so we will reserve that amount of memory. We have no easy way to tell what diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c index e50ad7eefc02..882104f43b3b 100644 --- a/arch/arm/kernel/efi.c +++ b/arch/arm/kernel/efi.c @@ -75,38 +75,13 @@ int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md) return 0; } -static unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata cpu_state_table = EFI_INVALID_TABLE_ADDR; const efi_config_table_type_t efi_arch_tables[] __initconst = { - {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, &screen_info_table}, {LINUX_EFI_ARM_CPU_STATE_TABLE_GUID, &cpu_state_table}, {} }; -static void __init load_screen_info_table(void) -{ - struct screen_info *si; - - if (screen_info_table != EFI_INVALID_TABLE_ADDR) { - si = early_memremap_ro(screen_info_table, sizeof(*si)); - if (!si) { - pr_err("Could not map screen_info config table\n"); - return; - } - screen_info = *si; - early_memunmap(si, sizeof(*si)); - - /* dummycon on ARM needs non-zero values for columns/lines */ - screen_info.orig_video_cols = 80; - screen_info.orig_video_lines = 25; - - if (memblock_is_map_memory(screen_info.lfb_base)) - memblock_mark_nomap(screen_info.lfb_base, - screen_info.lfb_size); - } -} - static void __init load_cpu_state_table(void) { if (cpu_state_table != EFI_INVALID_TABLE_ADDR) { @@ -145,7 +120,11 @@ void __init arm_efi_init(void) { efi_init(); - load_screen_info_table(); + if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) { + /* dummycon on ARM needs non-zero values for columns/lines */ + screen_info.orig_video_cols = 80; + screen_info.orig_video_lines = 25; + } /* ARM does not permit early mappings to persist across paging_init() */ efi_memmap_unmap(); diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index d6cf535d8352..8604473a85b8 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -84,12 +84,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1)); } -#define alloc_screen_info(x...) &screen_info - -static inline void free_screen_info(struct screen_info *si) -{ -} - #define EFI_ALLOC_ALIGN SZ_64K /* diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h index 174567b00ddb..60d6a170c18d 100644 --- a/arch/loongarch/include/asm/efi.h +++ b/arch/loongarch/include/asm/efi.h @@ -19,15 +19,6 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt); #define EFI_ALLOC_ALIGN SZ_64K #define EFI_RT_VIRTUAL_OFFSET CSR_DMW0_BASE -static inline struct screen_info *alloc_screen_info(void) -{ - return &screen_info; -} - -static inline void free_screen_info(struct screen_info *si) -{ -} - static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) { return ULONG_MAX; diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index a31329971133..d75ce73e8ff8 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -52,6 +52,27 @@ void __init efi_runtime_init(void) set_bit(EFI_RUNTIME_SERVICES, &efi.flags); } +unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; + +static void __init init_screen_info(void) +{ + struct screen_info *si; + + if (screen_info_table == EFI_INVALID_TABLE_ADDR) + return; + + si = early_memremap(screen_info_table, sizeof(*si)); + if (!si) { + pr_err("Could not map screen_info config table\n"); + return; + } + screen_info = *si; + memset(si, 0, sizeof(*si)); + early_memunmap(si, sizeof(*si)); + + memblock_reserve(screen_info.lfb_base, screen_info.lfb_size); +} + void __init efi_init(void) { int size; @@ -80,8 +101,7 @@ void __init efi_init(void) set_bit(EFI_CONFIG_TABLES, &efi.flags); - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) - memblock_reserve(screen_info.lfb_base, screen_info.lfb_size); + init_screen_info(); if (boot_memmap == EFI_INVALID_TABLE_ADDR) return; diff --git a/arch/riscv/include/asm/efi.h b/arch/riscv/include/asm/efi.h index f74879a8f1ea..d0570936cb8c 100644 --- a/arch/riscv/include/asm/efi.h +++ b/arch/riscv/include/asm/efi.h @@ -31,12 +31,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) return ULONG_MAX; } -#define alloc_screen_info(x...) (&screen_info) - -static inline void free_screen_info(struct screen_info *si) -{ -} - void efi_virtmap_load(void); void efi_virtmap_unload(void); diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c index 2fd770b499a3..1639159493e3 100644 --- a/drivers/firmware/efi/efi-init.c +++ b/drivers/firmware/efi/efi-init.c @@ -22,6 +22,8 @@ #include +unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR; + static int __init is_memory(efi_memory_desc_t *md) { if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC)) @@ -55,9 +57,22 @@ extern __weak const efi_config_table_type_t efi_arch_tables[]; static void __init init_screen_info(void) { - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI && - memblock_is_map_memory(screen_info.lfb_base)) - memblock_mark_nomap(screen_info.lfb_base, screen_info.lfb_size); + struct screen_info *si; + + if (screen_info_table != EFI_INVALID_TABLE_ADDR) { + si = early_memremap(screen_info_table, sizeof(*si)); + if (!si) { + pr_err("Could not map screen_info config table\n"); + return; + } + screen_info = *si; + memset(si, 0, sizeof(*si)); + early_memunmap(si, sizeof(*si)); + + if (memblock_is_map_memory(screen_info.lfb_base)) + memblock_mark_nomap(screen_info.lfb_base, + screen_info.lfb_size); + } } static int __init uefi_init(u64 efi_system_table) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index a46df5d1d094..951a42d27cf4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -58,6 +58,8 @@ static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata initrd = EFI_INVALID_TABLE_ADDR; +extern unsigned long screen_info_table; + struct mm_struct efi_mm = { .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, efi_mm.mmap_lock), .mm_users = ATOMIC_INIT(2), @@ -546,6 +548,9 @@ static const efi_config_table_type_t common_tables[] __initconst = { #endif #ifdef CONFIG_EFI_COCO_SECRET {LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" }, +#endif +#ifdef CONFIG_EFI_GENERIC_STUB + {LINUX_EFI_SCREEN_INFO_TABLE_GUID, &screen_info_table }, #endif {}, }; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 5b0ae755180e..3f44e272ff9c 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -81,7 +81,8 @@ lib-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdt.o \ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o +lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ + screen_info.o lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index 0131e3aaa605..1073dd947516 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -76,43 +76,6 @@ void efi_handle_post_ebs_state(void) &efi_entry_state->sctlr_after_ebs); } -static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID; - -struct screen_info *alloc_screen_info(void) -{ - struct screen_info *si; - efi_status_t status; - - /* - * Unlike on arm64, where we can directly fill out the screen_info - * structure from the stub, we need to allocate a buffer to hold - * its contents while we hand over to the kernel proper from the - * decompressor. - */ - status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, - sizeof(*si), (void **)&si); - - if (status != EFI_SUCCESS) - return NULL; - - status = efi_bs_call(install_configuration_table, - &screen_info_guid, si); - if (status == EFI_SUCCESS) - return si; - - efi_bs_call(free_pool, si); - return NULL; -} - -void free_screen_info(struct screen_info *si) -{ - if (!si) - return; - - efi_bs_call(install_configuration_table, &screen_info_guid, NULL); - efi_bs_call(free_pool, si); -} - efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index b55d1009d4c8..8521dc09c6ae 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -47,6 +47,15 @@ static u64 virtmap_base = EFI_RT_VIRTUAL_BASE; static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0); +struct screen_info * __weak alloc_screen_info(void) +{ + return &screen_info; +} + +void __weak free_screen_info(struct screen_info *si) +{ +} + static struct screen_info *setup_graphics(void) { efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index a30fb5d8ef05..a4cb51e2cae3 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -975,4 +975,7 @@ efi_enable_reset_attack_mitigation(void) { } void efi_retrieve_tpm2_eventlog(void); +struct screen_info *alloc_screen_info(void); +void free_screen_info(struct screen_info *si); + #endif diff --git a/drivers/firmware/efi/libstub/screen_info.c b/drivers/firmware/efi/libstub/screen_info.c new file mode 100644 index 000000000000..8e76a8b384ba --- /dev/null +++ b/drivers/firmware/efi/libstub/screen_info.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "efistub.h" + +/* + * There are two ways of populating the core kernel's struct screen_info via the stub: + * - using a configuration table, like below, which relies on the EFI init code + * to locate the table and copy the contents; + * - by linking directly to the core kernel's copy of the global symbol. + * + * The latter is preferred because it makes the EFIFB earlycon available very + * early, but it only works if the EFI stub is part of the core kernel image + * itself. The zboot decompressor can only use the configuration table + * approach. + * + * In order to support both methods from the same build of the EFI stub + * library, provide this dummy global definition of struct screen_info. If it + * is required to satisfy a link dependency, it means we need to override the + * __weak alloc and free methods with the ones below, and those will be pulled + * in as well. + */ +struct screen_info screen_info; + +static efi_guid_t screen_info_guid = LINUX_EFI_SCREEN_INFO_TABLE_GUID; + +struct screen_info *alloc_screen_info(void) +{ + struct screen_info *si; + efi_status_t status; + + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, + sizeof(*si), (void **)&si); + + if (status != EFI_SUCCESS) + return NULL; + + status = efi_bs_call(install_configuration_table, + &screen_info_guid, si); + if (status == EFI_SUCCESS) + return si; + + efi_bs_call(free_pool, si); + return NULL; +} + +void free_screen_info(struct screen_info *si) +{ + if (!si) + return; + + efi_bs_call(install_configuration_table, &screen_info_guid, NULL); + efi_bs_call(free_pool, si); +} diff --git a/include/linux/efi.h b/include/linux/efi.h index 929d559ad41d..dfa6cc8bbef9 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -403,7 +403,7 @@ void efi_native_runtime_setup(void); * structure that was populated by the stub based on the GOP protocol instance * associated with ConOut */ -#define LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) +#define LINUX_EFI_SCREEN_INFO_TABLE_GUID EFI_GUID(0xe03fc20a, 0x85dc, 0x406e, 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95) #define LINUX_EFI_ARM_CPU_STATE_TABLE_GUID EFI_GUID(0xef79e4aa, 0x3c3d, 0x4989, 0xb9, 0x02, 0x07, 0xa9, 0x43, 0xe5, 0x50, 0xd2) #define LINUX_EFI_LOADER_ENTRY_GUID EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f) #define LINUX_EFI_RANDOM_SEED_TABLE_GUID EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2, 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b) -- cgit v1.2.3 From da8dd0c75b3f82eb366eb1745fb473ea92c8c087 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Oct 2022 18:21:52 +0200 Subject: efi: libstub: Provide local implementations of strrchr() and memchr() Clone the implementations of strrchr() and memchr() in lib/string.c so we can use them in the standalone zboot decompressor app. These routines are used by the FDT handling code. Signed-off-by: Ard Biesheuvel --- arch/arm64/kernel/image-vars.h | 3 -- arch/riscv/kernel/image-vars.h | 2 -- drivers/firmware/efi/libstub/Makefile | 3 +- drivers/firmware/efi/libstub/string.c | 62 +++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index ace584e0b065..f31130ba0233 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -21,9 +21,6 @@ PROVIDE(__efistub_primary_entry_offset = primary_entry - _text); * linked at. The routines below are all implemented in assembler in a * position independent manner */ -PROVIDE(__efistub_memchr = __pi_memchr); -PROVIDE(__efistub_strcmp = __pi_strcmp); -PROVIDE(__efistub_strrchr = __pi_strrchr); PROVIDE(__efistub_dcache_clean_poc = __pi_dcache_clean_poc); PROVIDE(__efistub__text = _text); diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 4913abd6dd10..7e2962ef73f9 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -23,9 +23,7 @@ * linked at. The routines below are all implemented in assembler in a * position independent manner */ -__efistub_memchr = memchr; __efistub_strcmp = strcmp; -__efistub_strrchr = strrchr; __efistub__start = _start; __efistub__start_kernel = _start_kernel; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 3f44e272ff9c..4964d3106dcb 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -25,7 +25,8 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ $(call cc-option,-mbranch-protection=none) cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ - -fno-builtin -fpic \ + -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ + -DEFI_HAVE_STRCMP -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) cflags-$(CONFIG_RISCV) += -fpic cflags-$(CONFIG_LOONGARCH) += -fpie diff --git a/drivers/firmware/efi/libstub/string.c b/drivers/firmware/efi/libstub/string.c index 5a1519d435bf..168fe8e79abc 100644 --- a/drivers/firmware/efi/libstub/string.c +++ b/drivers/firmware/efi/libstub/string.c @@ -64,6 +64,28 @@ char *strstr(const char *s1, const char *s2) return NULL; } +#ifndef EFI_HAVE_STRCMP +/** + * strcmp - Compare two strings + * @cs: One string + * @ct: Another string + */ +int strcmp(const char *cs, const char *ct) +{ + unsigned char c1, c2; + + while (1) { + c1 = *cs++; + c2 = *ct++; + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + } + return 0; +} +#endif + /** * strncmp - Compare two length-limited strings * @cs: One string @@ -140,3 +162,43 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) return simple_strtoull(cp, endp, base); } + +#ifdef CONFIG_EFI_PARAMS_FROM_FDT +#ifndef EFI_HAVE_STRRCHR +/** + * strrchr - Find the last occurrence of a character in a string + * @s: The string to be searched + * @c: The character to search for + */ +char *strrchr(const char *s, int c) +{ + const char *last = NULL; + do { + if (*s == (char)c) + last = s; + } while (*s++); + return (char *)last; +} +#endif +#ifndef EFI_HAVE_MEMCHR +/** + * memchr - Find a character in an area of memory. + * @s: The memory area + * @c: The byte to search for + * @n: The size of the area. + * + * returns the address of the first occurrence of @c, or %NULL + * if @c is not found + */ +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *p = s; + while (n-- != 0) { + if ((unsigned char)c == *p++) { + return (void *)(p - 1); + } + } + return NULL; +} +#endif +#endif -- cgit v1.2.3 From 42c8ea3dca094ab82776ca706fb7a9cbe8ac3dc9 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Oct 2022 12:55:11 +0200 Subject: efi: libstub: Factor out EFI stub entrypoint into separate file In preparation for allowing the EFI zboot decompressor to reuse most of the EFI stub machinery, factor out the actual EFI PE/COFF entrypoint into a separate file. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/efi-stub-entry.c | 65 +++++++++++++++++++ drivers/firmware/efi/libstub/efi-stub.c | 89 ++++++++------------------- drivers/firmware/efi/libstub/efistub.h | 8 +++ 4 files changed, 100 insertions(+), 64 deletions(-) create mode 100644 drivers/firmware/efi/libstub/efi-stub-entry.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 4964d3106dcb..ea96405bfd31 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -83,7 +83,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ - screen_info.o + screen_info.o efi-stub-entry.o lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c new file mode 100644 index 000000000000..5245c4f031c0 --- /dev/null +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include "efistub.h" + +/* + * EFI entry point for the generic EFI stub used by ARM, arm64, RISC-V and + * LoongArch. This is the entrypoint that is described in the PE/COFF header + * of the core kernel. + */ +efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, + efi_system_table_t *systab) +{ + efi_loaded_image_t *image; + efi_status_t status; + unsigned long image_addr; + unsigned long image_size = 0; + /* addr/point and size pairs for memory management*/ + char *cmdline_ptr = NULL; + efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; + unsigned long reserve_addr = 0; + unsigned long reserve_size = 0; + + WRITE_ONCE(efi_system_table, systab); + + /* Check if we were booted by the EFI firmware */ + if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + return EFI_INVALID_PARAMETER; + + /* + * Get a handle to the loaded image protocol. This is used to get + * information about the running image, such as size and the command + * line. + */ + status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, + (void *)&image); + if (status != EFI_SUCCESS) { + efi_err("Failed to get loaded image protocol\n"); + return status; + } + + status = efi_handle_cmdline(image, &cmdline_ptr); + if (status != EFI_SUCCESS) + return status; + + efi_info("Booting Linux Kernel...\n"); + + status = handle_kernel_image(&image_addr, &image_size, + &reserve_addr, + &reserve_size, + image, handle); + if (status != EFI_SUCCESS) { + efi_err("Failed to relocate kernel\n"); + return status; + } + + status = efi_stub_common(handle, image, image_addr, cmdline_ptr); + + efi_free(image_size, image_addr); + efi_free(reserve_size, reserve_addr); + + return status; +} diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c index 8521dc09c6ae..2955c1ac6a36 100644 --- a/drivers/firmware/efi/libstub/efi-stub.c +++ b/drivers/firmware/efi/libstub/efi-stub.c @@ -115,61 +115,21 @@ static u32 get_supported_rt_services(void) return supported; } -/* - * EFI entry point for the arm/arm64 EFI stubs. This is the entrypoint - * that is described in the PE/COFF header. Most of the code is the same - * for both archictectures, with the arch-specific code provided in the - * handle_kernel_image() function. - */ -efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, - efi_system_table_t *sys_table_arg) +efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr) { - efi_loaded_image_t *image; - efi_status_t status; - unsigned long image_addr; - unsigned long image_size = 0; - /* addr/point and size pairs for memory management*/ - char *cmdline_ptr = NULL; int cmdline_size = 0; - efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; - unsigned long reserve_addr = 0; - unsigned long reserve_size = 0; - struct screen_info *si; - - efi_system_table = sys_table_arg; - - /* Check if we were booted by the EFI firmware */ - if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - status = EFI_INVALID_PARAMETER; - goto fail; - } - - status = check_platform_features(); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Get a handle to the loaded image protocol. This is used to get - * information about the running image, such as size and the command - * line. - */ - status = efi_bs_call(handle_protocol, handle, &loaded_image_proto, - (void *)&image); - if (status != EFI_SUCCESS) { - efi_err("Failed to get loaded image protocol\n"); - goto fail; - } + efi_status_t status; + char *cmdline; /* * Get the command line from EFI, using the LOADED_IMAGE * protocol. We are going to copy the command line into the * device tree, so this can be allocated anywhere. */ - cmdline_ptr = efi_convert_cmdline(image, &cmdline_size); - if (!cmdline_ptr) { + cmdline = efi_convert_cmdline(image, &cmdline_size); + if (!cmdline) { efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n"); - status = EFI_OUT_OF_RESOURCES; - goto fail; + return EFI_OUT_OF_RESOURCES; } if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || @@ -183,25 +143,34 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, } if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0) { - status = efi_parse_options(cmdline_ptr); + status = efi_parse_options(cmdline); if (status != EFI_SUCCESS) { efi_err("Failed to parse options\n"); goto fail_free_cmdline; } } - efi_info("Booting Linux Kernel...\n"); + *cmdline_ptr = cmdline; + return EFI_SUCCESS; - si = setup_graphics(); +fail_free_cmdline: + efi_bs_call(free_pool, cmdline_ptr); + return status; +} - status = handle_kernel_image(&image_addr, &image_size, - &reserve_addr, - &reserve_size, - image, handle); - if (status != EFI_SUCCESS) { - efi_err("Failed to relocate kernel\n"); - goto fail_free_screeninfo; - } +efi_status_t efi_stub_common(efi_handle_t handle, + efi_loaded_image_t *image, + unsigned long image_addr, + char *cmdline_ptr) +{ + struct screen_info *si; + efi_status_t status; + + status = check_platform_features(); + if (status != EFI_SUCCESS) + return status; + + si = setup_graphics(); efi_retrieve_tpm2_eventlog(); @@ -221,13 +190,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, status = efi_boot_kernel(handle, image, image_addr, cmdline_ptr); - efi_free(image_size, image_addr); - efi_free(reserve_size, reserve_addr); -fail_free_screeninfo: free_screen_info(si); -fail_free_cmdline: - efi_bs_call(free_pool, cmdline_ptr); -fail: return status; } diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index a4cb51e2cae3..fd6953ed2d0b 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -958,6 +958,14 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_loaded_image_t *image, efi_handle_t image_handle); +/* shared entrypoint between the normal stub and the zboot stub */ +efi_status_t efi_stub_common(efi_handle_t handle, + efi_loaded_image_t *image, + unsigned long image_addr, + char *cmdline_ptr); + +efi_status_t efi_handle_cmdline(efi_loaded_image_t *image, char **cmdline_ptr); + asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt_addr, unsigned long fdt_size); -- cgit v1.2.3 From 1f1ba325a23df6739b6b5e52f40ec1aa228108b2 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Oct 2022 14:28:31 +0200 Subject: efi: libstub: Add image code and data size to the zimage metadata In order to be able to switch from LoadImage() [which treats the supplied PE/COFF image as file input only, and reconstructs the memory image based on the section descriptors] to a mode where we allocate the memory directly, and invoke the image in place, we need to now how much memory to allocate beyond the end of the image. So copy this information from the payload's PE/COFF header to the end of the compressed version of the payload, so that the decompressor app can access it before performing the decompression itself. We'll also need to size of the code region once we switch arm64 to jumping to the kernel proper with MMU and caches enabled, so let's capture that information as well. Note that SizeOfCode does not account for the header, so we need SizeOfHeaders as well. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile.zboot | 22 ++++++++++------------ drivers/firmware/efi/libstub/zboot-header.S | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 3340b385a05b..43e9a4cab9f5 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -10,18 +10,17 @@ comp-type-$(CONFIG_KERNEL_LZO) := lzo comp-type-$(CONFIG_KERNEL_XZ) := xzkern comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22 -# in GZIP, the appended le32 carrying the uncompressed size is part of the -# format, but in other cases, we just append it at the end for convenience, -# causing the original tools to complain when checking image integrity. -# So disregard it when calculating the payload size in the zimage header. -zboot-method-y := $(comp-type-y)_with_size -zboot-size-len-y := 4 - -zboot-method-$(CONFIG_KERNEL_GZIP) := gzip -zboot-size-len-$(CONFIG_KERNEL_GZIP) := 0 +# Copy the SizeOfHeaders, SizeOfCode and SizeOfImage fields from the payload to +# the end of the compressed image. Note that this presupposes a PE header +# offset of 64 bytes, which is what arm64, RISC-V and LoongArch use. +quiet_cmd_compwithsize = $(quiet_cmd_$(comp-type-y)) + cmd_compwithsize = $(cmd_$(comp-type-y)) && ( \ + dd status=none if=$< bs=4 count=1 skip=37 ; \ + dd status=none if=$< bs=4 count=1 skip=23 ; \ + dd status=none if=$< bs=4 count=1 skip=36 ) >> $@ $(obj)/vmlinuz: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE - $(call if_changed,$(zboot-method-y)) + $(call if_changed,compwithsize) OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \ --rename-section .data=.gzdata,load,alloc,readonly,contents @@ -30,7 +29,6 @@ $(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE) \ -DZBOOT_EFI_PATH="\"$(realpath $(obj)/vmlinuz.efi.elf)\"" \ - -DZBOOT_SIZE_LEN=$(zboot-size-len-y) \ -DCOMP_TYPE="\"$(comp-type-y)\"" $(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE @@ -46,4 +44,4 @@ OBJCOPYFLAGS_vmlinuz.efi := -O binary $(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.elf FORCE $(call if_changed,objcopy) -targets += zboot-header.o vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi +targets += zboot-header.o vmlinuz.o vmlinuz.efi.elf vmlinuz.efi diff --git a/drivers/firmware/efi/libstub/zboot-header.S b/drivers/firmware/efi/libstub/zboot-header.S index 9e6fe061ab07..bc2d7750d7f1 100644 --- a/drivers/firmware/efi/libstub/zboot-header.S +++ b/drivers/firmware/efi/libstub/zboot-header.S @@ -17,7 +17,7 @@ __efistub_efi_zboot_header: .long MZ_MAGIC .ascii "zimg" // image type .long __efistub__gzdata_start - .Ldoshdr // payload offset - .long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size + .long __efistub__gzdata_size - 12 // payload size .long 0, 0 // reserved .asciz COMP_TYPE // compression type .org .Ldoshdr + 0x3c -- cgit v1.2.3 From 895bc3a135ffe9475de04b30e274f57a176cd3ef Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Oct 2022 14:57:32 +0200 Subject: efi: libstub: Factor out min alignment and preferred kernel load address Factor out the expressions that describe the preferred placement of the loaded image as well as the minimum alignment so we can reuse them in the decompressor. Signed-off-by: Ard Biesheuvel --- arch/arm64/include/asm/efi.h | 15 +++++++++++++++ arch/loongarch/include/asm/efi.h | 7 +++++++ arch/riscv/include/asm/efi.h | 11 +++++++++++ drivers/firmware/efi/libstub/arm64-stub.c | 11 +---------- drivers/firmware/efi/libstub/loongarch-stub.c | 3 ++- drivers/firmware/efi/libstub/riscv-stub.c | 15 +++------------ 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 8604473a85b8..925364586672 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -84,6 +84,21 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1)); } +static inline unsigned long efi_get_kimg_min_align(void) +{ + extern bool efi_nokaslr; + + /* + * Although relocatable kernels can fix up the misalignment with + * respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are + * subtly out of sync with those recorded in the vmlinux when kaslr is + * disabled but the image required relocation anyway. Therefore retain + * 2M alignment if KASLR was explicitly disabled, even if it was not + * going to be activated to begin with. + */ + return efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; +} + #define EFI_ALLOC_ALIGN SZ_64K /* diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h index 60d6a170c18d..5a470c8d2bbc 100644 --- a/arch/loongarch/include/asm/efi.h +++ b/arch/loongarch/include/asm/efi.h @@ -24,4 +24,11 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) return ULONG_MAX; } +static inline unsigned long efi_get_kimg_min_align(void) +{ + return SZ_2M; +} + +#define EFI_KIMG_PREFERRED_ADDRESS PHYSADDR(VMLINUX_LOAD_ADDRESS) + #endif /* _ASM_LOONGARCH_EFI_H */ diff --git a/arch/riscv/include/asm/efi.h b/arch/riscv/include/asm/efi.h index d0570936cb8c..a742868eb23c 100644 --- a/arch/riscv/include/asm/efi.h +++ b/arch/riscv/include/asm/efi.h @@ -31,6 +31,17 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) return ULONG_MAX; } +static inline unsigned long efi_get_kimg_min_align(void) +{ + /* + * RISC-V requires the kernel image to placed 2 MB aligned base for 64 + * bit and 4MB for 32 bit. + */ + return IS_ENABLED(CONFIG_64BIT) ? SZ_2M : SZ_4M; +} + +#define EFI_KIMG_PREFERRED_ADDRESS efi_get_kimg_min_align() + void efi_virtmap_load(void); void efi_virtmap_unload(void); diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index bcb21afd8aeb..f35c0e54e294 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -88,16 +88,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_status_t status; unsigned long kernel_size, kernel_memsize = 0; u32 phys_seed = 0; - - /* - * Although relocatable kernels can fix up the misalignment with - * respect to MIN_KIMG_ALIGN, the resulting virtual text addresses are - * subtly out of sync with those recorded in the vmlinux when kaslr is - * disabled but the image required relocation anyway. Therefore retain - * 2M alignment if KASLR was explicitly disabled, even if it was not - * going to be activated to begin with. - */ - u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN; + u64 min_kimg_align = efi_get_kimg_min_align(); if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index 32329f2a92f9..a2e55e5c4003 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -35,7 +35,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, kernel_addr = (unsigned long)&kernel_offset - kernel_offset; status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize, - PHYSADDR(VMLINUX_LOAD_ADDRESS), SZ_2M, 0x0); + EFI_KIMG_PREFERRED_ADDRESS, + efi_get_kimg_min_align(), 0x0); *image_addr = kernel_addr; *image_size = kernel_asize; diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c index b450ebf95977..c5a551f69a7f 100644 --- a/drivers/firmware/efi/libstub/riscv-stub.c +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -12,16 +12,6 @@ #include "efistub.h" -/* - * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and - * 4MB for 32 bit. - */ -#ifdef CONFIG_64BIT -#define MIN_KIMG_ALIGN SZ_2M -#else -#define MIN_KIMG_ALIGN SZ_4M -#endif - typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); static unsigned long hartid; @@ -125,9 +115,10 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * lowest possible memory region as long as the address and size meets * the alignment constraints. */ - preferred_addr = MIN_KIMG_ALIGN; + preferred_addr = EFI_KIMG_PREFERRED_ADDRESS; status = efi_relocate_kernel(image_addr, kernel_size, *image_size, - preferred_addr, MIN_KIMG_ALIGN, 0x0); + preferred_addr, efi_get_kimg_min_align(), + 0x0); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); -- cgit v1.2.3 From f1a116c055e3aee0c579d39bd777c9155e8bf5d1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 12 Oct 2022 20:23:46 +0200 Subject: efi/riscv: libstub: Split off kernel image relocation for builtin stub The RISC-V build of the EFI stub is part of the core kernel image, and therefore accesses section markers directly when it needs to figure out the size of the various section. The zboot decompressor does not have access to those symbols, but doesn't really need that either. So let's move handle_kernel_image() into a separate file (or rather, move everything else into a separate file) so that the zboot build does not pull in unused code that links to symbols that it does not define. Signed-off-by: Ard Biesheuvel --- arch/riscv/include/asm/efi.h | 2 + drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/riscv-stub.c | 81 +++---------------------- drivers/firmware/efi/libstub/riscv.c | 98 +++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 drivers/firmware/efi/libstub/riscv.c diff --git a/arch/riscv/include/asm/efi.h b/arch/riscv/include/asm/efi.h index a742868eb23c..6cbc7437d886 100644 --- a/arch/riscv/include/asm/efi.h +++ b/arch/riscv/include/asm/efi.h @@ -45,4 +45,6 @@ static inline unsigned long efi_get_kimg_min_align(void) void efi_virtmap_load(void); void efi_virtmap_unload(void); +unsigned long stext_offset(void); + #endif /* _ASM_EFI_H */ diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index ea96405bfd31..5ec0635b4987 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -88,7 +88,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o lib-$(CONFIG_X86) += x86-stub.o -lib-$(CONFIG_RISCV) += riscv-stub.o +lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o lib-$(CONFIG_LOONGARCH) += loongarch-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c index c5a551f69a7f..145c9f0ba217 100644 --- a/drivers/firmware/efi/libstub/riscv-stub.c +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -4,7 +4,6 @@ */ #include -#include #include #include @@ -12,82 +11,16 @@ #include "efistub.h" -typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); - -static unsigned long hartid; - -static int get_boot_hartid_from_fdt(void) -{ - const void *fdt; - int chosen_node, len; - const void *prop; - - fdt = get_efi_config_table(DEVICE_TREE_GUID); - if (!fdt) - return -EINVAL; - - chosen_node = fdt_path_offset(fdt, "/chosen"); - if (chosen_node < 0) - return -EINVAL; - - prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); - if (!prop) - return -EINVAL; - - if (len == sizeof(u32)) - hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop); - else if (len == sizeof(u64)) - hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop)); - else - return -EINVAL; - - return 0; -} - -static efi_status_t get_boot_hartid_from_efi(void) +unsigned long stext_offset(void) { - efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; - struct riscv_efi_boot_protocol *boot_protocol; - efi_status_t status; - - status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL, - (void **)&boot_protocol); - if (status != EFI_SUCCESS) - return status; - return efi_call_proto(boot_protocol, get_boot_hartid, &hartid); -} - -efi_status_t check_platform_features(void) -{ - efi_status_t status; - int ret; - - status = get_boot_hartid_from_efi(); - if (status != EFI_SUCCESS) { - ret = get_boot_hartid_from_fdt(); - if (ret) { - efi_err("Failed to get boot hartid!\n"); - return EFI_UNSUPPORTED; - } - } - return EFI_SUCCESS; -} - -void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, - unsigned long fdt_size) -{ - unsigned long stext_offset = _start_kernel - _start; - unsigned long kernel_entry = entrypoint + stext_offset; - jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; - /* - * Jump to real kernel here with following constraints. - * 1. MMU should be disabled. - * 2. a0 should contain hartid - * 3. a1 should DT address + * When built as part of the kernel, the EFI stub cannot branch to the + * kernel proper via the image header, as the PE/COFF header is + * strictly not part of the in-memory presentation of the image, only + * of the file representation. So instead, we need to jump to the + * actual entrypoint in the .text region of the image. */ - csr_write(CSR_SATP, 0); - jump_kernel(hartid, fdt); + return _start_kernel - _start; } efi_status_t handle_kernel_image(unsigned long *image_addr, diff --git a/drivers/firmware/efi/libstub/riscv.c b/drivers/firmware/efi/libstub/riscv.c new file mode 100644 index 000000000000..8022b104c3e6 --- /dev/null +++ b/drivers/firmware/efi/libstub/riscv.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Western Digital Corporation or its affiliates. + */ + +#include +#include + +#include +#include + +#include "efistub.h" + +typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); + +static unsigned long hartid; + +static int get_boot_hartid_from_fdt(void) +{ + const void *fdt; + int chosen_node, len; + const void *prop; + + fdt = get_efi_config_table(DEVICE_TREE_GUID); + if (!fdt) + return -EINVAL; + + chosen_node = fdt_path_offset(fdt, "/chosen"); + if (chosen_node < 0) + return -EINVAL; + + prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); + if (!prop) + return -EINVAL; + + if (len == sizeof(u32)) + hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop); + else if (len == sizeof(u64)) + hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop)); + else + return -EINVAL; + + return 0; +} + +static efi_status_t get_boot_hartid_from_efi(void) +{ + efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; + struct riscv_efi_boot_protocol *boot_protocol; + efi_status_t status; + + status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL, + (void **)&boot_protocol); + if (status != EFI_SUCCESS) + return status; + return efi_call_proto(boot_protocol, get_boot_hartid, &hartid); +} + +efi_status_t check_platform_features(void) +{ + efi_status_t status; + int ret; + + status = get_boot_hartid_from_efi(); + if (status != EFI_SUCCESS) { + ret = get_boot_hartid_from_fdt(); + if (ret) { + efi_err("Failed to get boot hartid!\n"); + return EFI_UNSUPPORTED; + } + } + return EFI_SUCCESS; +} + +unsigned long __weak stext_offset(void) +{ + /* + * This fallback definition is used by the EFI zboot stub, which loads + * the entire image so it can branch via the image header at offset #0. + */ + return 0; +} + +void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, + unsigned long fdt_size) +{ + unsigned long kernel_entry = entrypoint + stext_offset(); + jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; + + /* + * Jump to real kernel here with following constraints. + * 1. MMU should be disabled. + * 2. a0 should contain hartid + * 3. a1 should DT address + */ + csr_write(CSR_SATP, 0); + jump_kernel(hartid, fdt); +} -- cgit v1.2.3 From d9ffe524a538720328b5bf98733a6c641e236bc8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 13 Oct 2022 10:17:20 +0200 Subject: efi/arm64: libstub: Split off kernel image relocation for builtin stub The arm64 build of the EFI stub is part of the core kernel image, and therefore accesses section markers directly when it needs to figure out the size of the various section. The zboot decompressor does not have access to those symbols, but doesn't really need that either. So let's move handle_kernel_image() into a separate file (or rather, move everything else into a separate file) so that the zboot build does not pull in unused code that links to symbols that it does not define. While at it, introduce a helper routine that the generic zboot loader will need to invoke after decompressing the image but before invoking it, to ensure that the I-side view of memory is consistent. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/arm64-entry.S | 10 +++++ drivers/firmware/efi/libstub/arm64-stub.c | 28 -------------- drivers/firmware/efi/libstub/arm64.c | 61 ++++++++++++++++++++++++++++++ drivers/firmware/efi/libstub/efistub.h | 4 ++ 5 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 drivers/firmware/efi/libstub/arm64.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 5ec0635b4987..63e1eddf996b 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -86,7 +86,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ screen_info.o efi-stub-entry.o lib-$(CONFIG_ARM) += arm32-stub.o -lib-$(CONFIG_ARM64) += arm64-stub.o arm64-entry.o +lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o lib-$(CONFIG_X86) += x86-stub.o lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o lib-$(CONFIG_LOONGARCH) += loongarch-stub.o diff --git a/drivers/firmware/efi/libstub/arm64-entry.S b/drivers/firmware/efi/libstub/arm64-entry.S index 87082b222a87..b5c17e89a4fc 100644 --- a/drivers/firmware/efi/libstub/arm64-entry.S +++ b/drivers/firmware/efi/libstub/arm64-entry.S @@ -8,6 +8,16 @@ #include #include + /* + * The entrypoint of a arm64 bare metal image is at offset #0 of the + * image, so this is a reasonable default for primary_entry_offset. + * Only when the EFI stub is integrated into the core kernel, it is not + * guaranteed that the PE/COFF header has been copied to memory too, so + * in this case, primary_entry_offset should be overridden by the + * linker and point to primary_entry() directly. + */ + .weak primary_entry_offset + SYM_CODE_START(efi_enter_kernel) /* * efi_pe_entry() will have copied the kernel image if necessary and we diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index f35c0e54e294..c14028b625b4 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -11,37 +11,9 @@ #include #include #include -#include #include "efistub.h" -efi_status_t check_platform_features(void) -{ - u64 tg; - - /* - * If we have 48 bits of VA space for TTBR0 mappings, we can map the - * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is - * unnecessary. - */ - if (VA_BITS_MIN >= 48) - efi_novamap = true; - - /* UEFI mandates support for 4 KB granularity, no need to check */ - if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) - return EFI_SUCCESS; - - tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf; - if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) { - if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) - efi_err("This 64 KB granular kernel is not supported by your CPU\n"); - else - efi_err("This 16 KB granular kernel is not supported by your CPU\n"); - return EFI_UNSUPPORTED; - } - return EFI_SUCCESS; -} - /* * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail * to provide space, and fail to zero it). Check for this condition by double diff --git a/drivers/firmware/efi/libstub/arm64.c b/drivers/firmware/efi/libstub/arm64.c new file mode 100644 index 000000000000..d2e94972c5fa --- /dev/null +++ b/drivers/firmware/efi/libstub/arm64.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013, 2014 Linaro Ltd; + * + * This file implements the EFI boot stub for the arm64 kernel. + * Adapted from ARM version by Mark Salter + */ + + +#include +#include +#include +#include + +#include "efistub.h" + +efi_status_t check_platform_features(void) +{ + u64 tg; + + /* + * If we have 48 bits of VA space for TTBR0 mappings, we can map the + * UEFI runtime regions 1:1 and so calling SetVirtualAddressMap() is + * unnecessary. + */ + if (VA_BITS_MIN >= 48) + efi_novamap = true; + + /* UEFI mandates support for 4 KB granularity, no need to check */ + if (IS_ENABLED(CONFIG_ARM64_4K_PAGES)) + return EFI_SUCCESS; + + tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_EL1_TGRAN_SHIFT) & 0xf; + if (tg < ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MIN || tg > ID_AA64MMFR0_EL1_TGRAN_SUPPORTED_MAX) { + if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) + efi_err("This 64 KB granular kernel is not supported by your CPU\n"); + else + efi_err("This 16 KB granular kernel is not supported by your CPU\n"); + return EFI_UNSUPPORTED; + } + return EFI_SUCCESS; +} + +void efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size) +{ + u32 ctr = read_cpuid_effective_cachetype(); + u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr, + CTR_EL0_DminLine_SHIFT); + + do { + asm("dc civac, %0" :: "r"(image_base)); + image_base += lsize; + alloc_size -= lsize; + } while (alloc_size >= lsize); + + asm("ic ialluis"); + dsb(ish); + isb(); +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index fd6953ed2d0b..b5e1095b0016 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -986,4 +986,8 @@ void efi_retrieve_tpm2_eventlog(void); struct screen_info *alloc_screen_info(void); void free_screen_info(struct screen_info *si); +void efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size); + #endif -- cgit v1.2.3 From 0efb61c89fa021dfcdb92f22bbc9a7cb3f0fe3fe Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 13 Oct 2022 12:07:27 +0200 Subject: efi/loongarch: Don't jump to kernel entry via the old image Currently, the EFI entry code for LoongArch is set up to copy the executable image to the preferred offset, but instead of branching directly into that image, it branches to the local copy of kernel_entry, and relies on the logic in that function to switch to the link time address instead. This is a bit sloppy, and not something we can support once we merge the EFI decompressor with the EFI stub. So let's clean this up a bit, by adding a helper that computes the offset of kernel_entry from the start of the image, and simply adding the result to VMLINUX_LOAD_ADDRESS. And considering that we cannot execute from anywhere else anyway, let's avoid efi_relocate_kernel() and just allocate the pages instead. Signed-off-by: Ard Biesheuvel --- arch/loongarch/include/asm/efi.h | 2 ++ drivers/firmware/efi/libstub/loongarch-stub.c | 33 +++++++++++++++++++-------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h index 5a470c8d2bbc..97f16e60c6ff 100644 --- a/arch/loongarch/include/asm/efi.h +++ b/arch/loongarch/include/asm/efi.h @@ -31,4 +31,6 @@ static inline unsigned long efi_get_kimg_min_align(void) #define EFI_KIMG_PREFERRED_ADDRESS PHYSADDR(VMLINUX_LOAD_ADDRESS) +unsigned long kernel_entry_address(void); + #endif /* _ASM_LOONGARCH_EFI_H */ diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index a2e55e5c4003..6b30fc281458 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -29,18 +29,27 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_loaded_image_t *image, efi_handle_t image_handle) { + int nr_pages = round_up(kernel_asize, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; + efi_physical_addr_t kernel_addr = EFI_KIMG_PREFERRED_ADDRESS; efi_status_t status; - unsigned long kernel_addr = 0; - kernel_addr = (unsigned long)&kernel_offset - kernel_offset; - - status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize, - EFI_KIMG_PREFERRED_ADDRESS, - efi_get_kimg_min_align(), 0x0); + /* + * Allocate space for the kernel image at the preferred offset. This is + * the only location in memory from where we can execute the image, so + * no point in falling back to another allocation. + */ + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, + EFI_LOADER_DATA, nr_pages, &kernel_addr); + if (status != EFI_SUCCESS) + return status; - *image_addr = kernel_addr; + *image_addr = EFI_KIMG_PREFERRED_ADDRESS; *image_size = kernel_asize; + memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS, + (void *)&kernel_offset - kernel_offset, + kernel_fsize); + return status; } @@ -64,6 +73,13 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) return EFI_SUCCESS; } +unsigned long kernel_entry_address(void) +{ + unsigned long base = (unsigned long)&kernel_offset - kernel_offset; + + return (unsigned long)&kernel_entry - base + VMLINUX_LOAD_ADDRESS; +} + efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, unsigned long kernel_addr, char *cmdline_ptr) { @@ -95,8 +111,7 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); - real_kernel_entry = (kernel_entry_t) - ((unsigned long)&kernel_entry - kernel_addr + VMLINUX_LOAD_ADDRESS); + real_kernel_entry = (void *)kernel_entry_address(); real_kernel_entry(true, (unsigned long)cmdline_ptr, (unsigned long)efi_system_table); -- cgit v1.2.3 From d729b554e17904e16498ab5c36122d70406f70ae Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 13 Oct 2022 12:39:37 +0200 Subject: efi/loongarch: libstub: Split off kernel image relocation for builtin stub The LoongArch build of the EFI stub is part of the core kernel image, and therefore accesses section markers directly when it needs to figure out the size of the various section. The zboot decompressor does not have access to those symbols, but doesn't really need that either. So let's move handle_kernel_image() into a separate file (or rather, move everything else into a separate file) so that the zboot build does not pull in unused code that links to symbols that it does not define. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/loongarch-stub.c | 67 +--------------------- drivers/firmware/efi/libstub/loongarch.c | 80 +++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 67 deletions(-) create mode 100644 drivers/firmware/efi/libstub/loongarch.c diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 63e1eddf996b..402dfb30ddc7 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -89,7 +89,7 @@ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o arm64-entry.o lib-$(CONFIG_X86) += x86-stub.o lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o -lib-$(CONFIG_LOONGARCH) += loongarch-stub.o +lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index 6b30fc281458..eee7ed43cdfb 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -9,18 +9,10 @@ #include #include "efistub.h" -typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline, - unsigned long systab); - extern int kernel_asize; extern int kernel_fsize; extern int kernel_offset; -extern kernel_entry_t kernel_entry; - -efi_status_t check_platform_features(void) -{ - return EFI_SUCCESS; -} +extern int kernel_entry; efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, @@ -53,66 +45,9 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, return status; } -struct exit_boot_struct { - efi_memory_desc_t *runtime_map; - int runtime_entry_count; -}; - -static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) -{ - struct exit_boot_struct *p = priv; - - /* - * Update the memory map with virtual addresses. The function will also - * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME - * entries so that we can pass it straight to SetVirtualAddressMap() - */ - efi_get_virtmap(map->map, map->map_size, map->desc_size, - p->runtime_map, &p->runtime_entry_count); - - return EFI_SUCCESS; -} - unsigned long kernel_entry_address(void) { unsigned long base = (unsigned long)&kernel_offset - kernel_offset; return (unsigned long)&kernel_entry - base + VMLINUX_LOAD_ADDRESS; } - -efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, - unsigned long kernel_addr, char *cmdline_ptr) -{ - kernel_entry_t real_kernel_entry; - struct exit_boot_struct priv; - unsigned long desc_size; - efi_status_t status; - u32 desc_ver; - - status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); - if (status != EFI_SUCCESS) { - efi_err("Unable to retrieve UEFI memory map.\n"); - return status; - } - - efi_info("Exiting boot services\n"); - - efi_novamap = false; - status = efi_exit_boot_services(handle, &priv, exit_boot_func); - if (status != EFI_SUCCESS) - return status; - - /* Install the new virtual address map */ - efi_rt_call(set_virtual_address_map, - priv.runtime_entry_count * desc_size, desc_size, - desc_ver, priv.runtime_map); - - /* Config Direct Mapping */ - csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); - csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); - - real_kernel_entry = (void *)kernel_entry_address(); - - real_kernel_entry(true, (unsigned long)cmdline_ptr, - (unsigned long)efi_system_table); -} diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c new file mode 100644 index 000000000000..807cba2693fc --- /dev/null +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Yun Liu + * Huacai Chen + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include "efistub.h" + +typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline, + unsigned long systab); + +efi_status_t check_platform_features(void) +{ + return EFI_SUCCESS; +} + +struct exit_boot_struct { + efi_memory_desc_t *runtime_map; + int runtime_entry_count; +}; + +static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) +{ + struct exit_boot_struct *p = priv; + + /* + * Update the memory map with virtual addresses. The function will also + * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME + * entries so that we can pass it straight to SetVirtualAddressMap() + */ + efi_get_virtmap(map->map, map->map_size, map->desc_size, + p->runtime_map, &p->runtime_entry_count); + + return EFI_SUCCESS; +} + +unsigned long __weak kernel_entry_address(void) +{ + return *(unsigned long *)(PHYSADDR(VMLINUX_LOAD_ADDRESS) + 8); +} + +efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, + unsigned long kernel_addr, char *cmdline_ptr) +{ + kernel_entry_t real_kernel_entry; + struct exit_boot_struct priv; + unsigned long desc_size; + efi_status_t status; + u32 desc_ver; + + status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); + if (status != EFI_SUCCESS) { + efi_err("Unable to retrieve UEFI memory map.\n"); + return status; + } + + efi_info("Exiting boot services\n"); + + efi_novamap = false; + status = efi_exit_boot_services(handle, &priv, exit_boot_func); + if (status != EFI_SUCCESS) + return status; + + /* Install the new virtual address map */ + efi_rt_call(set_virtual_address_map, + priv.runtime_entry_count * desc_size, desc_size, + desc_ver, priv.runtime_map); + + /* Config Direct Mapping */ + csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); + csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); + + real_kernel_entry = (void *)kernel_entry_address(); + + real_kernel_entry(true, (unsigned long)cmdline_ptr, + (unsigned long)efi_system_table); +} -- cgit v1.2.3 From c51e97e7f1295d3cf42682565506047f08dfc99b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 13 Oct 2022 12:42:07 +0200 Subject: efi: libstub: Merge zboot decompressor with the ordinary stub Even though our EFI zboot decompressor is pedantically spec compliant and idiomatic for EFI image loaders, calling LoadImage() and StartImage() for the nested image is a bit of a burden. Not only does it create workflow issues for the distros (as both the inner and outer PE/COFF images need to be signed for secure boot), it also copies the image around in memory numerous times: - first, the image is decompressed into a buffer; - the buffer is consumed by LoadImage(), which copies the sections into a newly allocated memory region to hold the executable image; - once the EFI stub is invoked by StartImage(), it will also move the image in memory in case of KASLR, mirrored memory or if the image must execute from a certain a priori defined address. There are only two EFI spec compliant ways to load code into memory and execute it: - use LoadImage() and StartImage(), - call ExitBootServices() and take ownership of the entire system, after which anything goes. Given that the EFI zboot decompressor always invokes the EFI stub, and given that both are built from the same set of objects, let's merge the two, so that we can avoid LoadImage()/StartImage but still load our image into memory without breaking the above rules. This also means we can decompress the image directly into its final location, which could be randomized or meet other platform specific constraints that LoadImage() does not know how to adhere to. It also means that, even if the encapsulated image still has the EFI stub incorporated as well, it does not need to be signed for secure boot when wrapping it in the EFI zboot decompressor. In the future, we might decide to retire the EFI stub attached to the decompressed image, but for the time being, they can happily coexist. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/file.c | 18 --- drivers/firmware/efi/libstub/zboot.c | 284 +++++++++-------------------------- include/linux/efi.h | 1 - 3 files changed, 74 insertions(+), 229 deletions(-) diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index f756c61396e9..246ccc5b015d 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -66,28 +66,10 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, static efi_status_t efi_open_volume(efi_loaded_image_t *image, efi_file_protocol_t **fh) { - struct efi_vendor_dev_path *dp = image->file_path; - efi_guid_t li_proto = LOADED_IMAGE_PROTOCOL_GUID; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_simple_file_system_protocol_t *io; efi_status_t status; - // If we are using EFI zboot, we should look for the file system - // protocol on the parent image's handle instead - if (IS_ENABLED(CONFIG_EFI_ZBOOT) && - image->parent_handle != NULL && - dp != NULL && - dp->header.type == EFI_DEV_MEDIA && - dp->header.sub_type == EFI_DEV_MEDIA_VENDOR && - !efi_guidcmp(dp->vendorguid, LINUX_EFI_ZBOOT_MEDIA_GUID)) { - status = efi_bs_call(handle_protocol, image->parent_handle, - &li_proto, (void *)&image); - if (status != EFI_SUCCESS) { - efi_err("Failed to locate parent image handle\n"); - return status; - } - } - status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, (void **)&io); if (status != EFI_SUCCESS) { diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 5f41a5b17d6e..66be5fdc6b58 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -37,247 +37,111 @@ static void error(char *x) efi_err("EFI decompressor: %s\n", x); } -static efi_status_t __efiapi -load_file(efi_load_file_protocol_t *this, efi_device_path_protocol_t *rem, - bool boot_policy, unsigned long *bufsize, void *buffer) +static unsigned long alloc_preferred_address(unsigned long alloc_size) { - unsigned long compressed_size = _gzdata_end - _gzdata_start; - struct efi_vendor_dev_path *vendor_dp; - bool decompress = false; - unsigned long size; - int ret; - - if (rem == NULL || bufsize == NULL) - return EFI_INVALID_PARAMETER; - - if (boot_policy) - return EFI_UNSUPPORTED; - - // Look for our vendor media device node in the remaining file path - if (rem->type == EFI_DEV_MEDIA && - rem->sub_type == EFI_DEV_MEDIA_VENDOR) { - vendor_dp = container_of(rem, struct efi_vendor_dev_path, header); - if (efi_guidcmp(vendor_dp->vendorguid, LINUX_EFI_ZBOOT_MEDIA_GUID)) - return EFI_NOT_FOUND; - - decompress = true; - rem = (void *)(vendor_dp + 1); - } - - if (rem->type != EFI_DEV_END_PATH || - rem->sub_type != EFI_DEV_END_ENTIRE) - return EFI_NOT_FOUND; - - // The uncompressed size of the payload is appended to the raw bit - // stream, and may therefore appear misaligned in memory - size = decompress ? get_unaligned_le32(_gzdata_end - 4) - : compressed_size; - if (buffer == NULL || *bufsize < size) { - *bufsize = size; - return EFI_BUFFER_TOO_SMALL; - } - - if (decompress) { - ret = __decompress(_gzdata_start, compressed_size, NULL, NULL, - buffer, size, NULL, error); - if (ret < 0) { - error("Decompression failed"); - return EFI_DEVICE_ERROR; - } - } else { - memcpy(buffer, _gzdata_start, compressed_size); - } - - return EFI_SUCCESS; -} - -// Return the length in bytes of the device path up to the first end node. -static int device_path_length(const efi_device_path_protocol_t *dp) -{ - int len = 0; - - while (dp->type != EFI_DEV_END_PATH) { - len += dp->length; - dp = (void *)((u8 *)dp + dp->length); - } - return len; -} - -static void append_rel_offset_node(efi_device_path_protocol_t **dp, - unsigned long start, unsigned long end) -{ - struct efi_rel_offset_dev_path *rodp = (void *)*dp; - - rodp->header.type = EFI_DEV_MEDIA; - rodp->header.sub_type = EFI_DEV_MEDIA_REL_OFFSET; - rodp->header.length = sizeof(struct efi_rel_offset_dev_path); - rodp->reserved = 0; - rodp->starting_offset = start; - rodp->ending_offset = end; - - *dp = (void *)(rodp + 1); -} - -static void append_ven_media_node(efi_device_path_protocol_t **dp, - efi_guid_t *guid) -{ - struct efi_vendor_dev_path *vmdp = (void *)*dp; - - vmdp->header.type = EFI_DEV_MEDIA; - vmdp->header.sub_type = EFI_DEV_MEDIA_VENDOR; - vmdp->header.length = sizeof(struct efi_vendor_dev_path); - vmdp->vendorguid = *guid; +#ifdef EFI_KIMG_PREFERRED_ADDRESS + efi_physical_addr_t efi_addr = EFI_KIMG_PREFERRED_ADDRESS; - *dp = (void *)(vmdp + 1); + if (efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, + alloc_size / EFI_PAGE_SIZE, &efi_addr) == EFI_SUCCESS) + return efi_addr; +#endif + return ULONG_MAX; } -static void append_end_node(efi_device_path_protocol_t **dp) +void __weak efi_cache_sync_image(unsigned long image_base, + unsigned long alloc_size, + unsigned long code_size) { - (*dp)->type = EFI_DEV_END_PATH; - (*dp)->sub_type = EFI_DEV_END_ENTIRE; - (*dp)->length = sizeof(struct efi_generic_dev_path); - - ++*dp; + // Provided by the arch to perform the cache maintenance necessary for + // executable code loaded into memory to be safe for execution. } asmlinkage efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) { - struct efi_mem_mapped_dev_path mmdp = { - .header.type = EFI_DEV_HW, - .header.sub_type = EFI_DEV_MEM_MAPPED, - .header.length = sizeof(struct efi_mem_mapped_dev_path) - }; - efi_device_path_protocol_t *parent_dp, *dpp, *lf2_dp, *li_dp; - efi_load_file2_protocol_t zboot_load_file2; - efi_loaded_image_t *parent, *child; - unsigned long exit_data_size; - efi_handle_t child_handle; - efi_handle_t zboot_handle; - efi_char16_t *exit_data; + unsigned long compressed_size = _gzdata_end - _gzdata_start; + unsigned long image_base, alloc_size, code_size; + efi_loaded_image_t *image; efi_status_t status; - void *dp_alloc; - int dp_len; + char *cmdline_ptr; + int ret; WRITE_ONCE(efi_system_table, systab); free_mem_ptr = (unsigned long)&zboot_heap; free_mem_end_ptr = free_mem_ptr + sizeof(zboot_heap); - exit_data = NULL; - exit_data_size = 0; - status = efi_bs_call(handle_protocol, handle, - &LOADED_IMAGE_PROTOCOL_GUID, (void **)&parent); + &LOADED_IMAGE_PROTOCOL_GUID, (void **)&image); if (status != EFI_SUCCESS) { error("Failed to locate parent's loaded image protocol"); return status; } - status = efi_bs_call(handle_protocol, handle, - &LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID, - (void **)&parent_dp); - if (status != EFI_SUCCESS || parent_dp == NULL) { - // Create a MemoryMapped() device path node to describe - // the parent image if no device path was provided. - mmdp.memory_type = parent->image_code_type; - mmdp.starting_addr = (unsigned long)parent->image_base; - mmdp.ending_addr = (unsigned long)parent->image_base + - parent->image_size - 1; - parent_dp = &mmdp.header; - dp_len = sizeof(mmdp); - } else { - dp_len = device_path_length(parent_dp); - } - - // Allocate some pool memory for device path protocol data - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, - 2 * (dp_len + sizeof(struct efi_rel_offset_dev_path) + - sizeof(struct efi_generic_dev_path)) + - sizeof(struct efi_vendor_dev_path), - (void **)&dp_alloc); - if (status != EFI_SUCCESS) { - error("Failed to allocate device path pool memory"); + status = efi_handle_cmdline(image, &cmdline_ptr); + if (status != EFI_SUCCESS) return status; - } - - // Create a device path describing the compressed payload in this image - // <...parent_dp...>/Offset(, ) - lf2_dp = memcpy(dp_alloc, parent_dp, dp_len); - dpp = (void *)((u8 *)lf2_dp + dp_len); - append_rel_offset_node(&dpp, - (unsigned long)(_gzdata_start - efi_zboot_header), - (unsigned long)(_gzdata_end - efi_zboot_header - 1)); - append_end_node(&dpp); - // Create a device path describing the decompressed payload in this image - // <...parent_dp...>/Offset(, )/VenMedia(ZBOOT_MEDIA_GUID) - dp_len += sizeof(struct efi_rel_offset_dev_path); - li_dp = memcpy(dpp, lf2_dp, dp_len); - dpp = (void *)((u8 *)li_dp + dp_len); - append_ven_media_node(&dpp, &LINUX_EFI_ZBOOT_MEDIA_GUID); - append_end_node(&dpp); - - zboot_handle = NULL; - zboot_load_file2.load_file = load_file; - status = efi_bs_call(install_multiple_protocol_interfaces, - &zboot_handle, - &EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp, - &EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2, - NULL); - if (status != EFI_SUCCESS) { - error("Failed to install LoadFile2 protocol and device path"); - goto free_dpalloc; - } - - status = efi_bs_call(load_image, false, handle, li_dp, NULL, 0, - &child_handle); - if (status != EFI_SUCCESS) { - error("Failed to load image"); - goto uninstall_lf2; - } + efi_info("Decompressing Linux Kernel...\n"); + + // SizeOfImage from the compressee's PE/COFF header + alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4), + EFI_ALLOC_ALIGN); + + // SizeOfHeaders and SizeOfCode from the compressee's PE/COFF header + code_size = get_unaligned_le32(_gzdata_end - 8) + + get_unaligned_le32(_gzdata_end - 12); + + // If the architecture has a preferred address for the image, + // try that first. + image_base = alloc_preferred_address(alloc_size); + if (image_base == ULONG_MAX) { + unsigned long min_kimg_align = efi_get_kimg_min_align(); + u32 seed = U32_MAX; + + if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { + // Setting the random seed to 0x0 is the same as + // allocating as low as possible + seed = 0; + } else if (efi_nokaslr) { + efi_info("KASLR disabled on kernel command line\n"); + } else { + status = efi_get_random_bytes(sizeof(seed), (u8 *)&seed); + if (status == EFI_NOT_FOUND) { + efi_info("EFI_RNG_PROTOCOL unavailable\n"); + efi_nokaslr = true; + } else if (status != EFI_SUCCESS) { + efi_err("efi_get_random_bytes() failed (0x%lx)\n", + status); + efi_nokaslr = true; + } + } - status = efi_bs_call(handle_protocol, child_handle, - &LOADED_IMAGE_PROTOCOL_GUID, (void **)&child); - if (status != EFI_SUCCESS) { - error("Failed to locate child's loaded image protocol"); - goto unload_image; + status = efi_random_alloc(alloc_size, min_kimg_align, &image_base, + seed, EFI_LOADER_CODE); + if (status != EFI_SUCCESS) { + efi_err("Failed to allocate memory\n"); + goto free_cmdline; + } } - // Copy the kernel command line - child->load_options = parent->load_options; - child->load_options_size = parent->load_options_size; - - status = efi_bs_call(start_image, child_handle, &exit_data_size, - &exit_data); - if (status != EFI_SUCCESS) { - error("StartImage() returned with error:"); - if (exit_data_size > 0) - efi_err("%ls\n", exit_data); - - // If StartImage() returns EFI_SECURITY_VIOLATION, the image is - // not unloaded so we need to do it by hand. - if (status == EFI_SECURITY_VIOLATION) -unload_image: - efi_bs_call(unload_image, child_handle); + // Decompress the payload into the newly allocated buffer. + ret = __decompress(_gzdata_start, compressed_size, NULL, NULL, + (void *)image_base, alloc_size, NULL, error); + if (ret < 0) { + error("Decompression failed"); + status = EFI_DEVICE_ERROR; + goto free_image; } -uninstall_lf2: - efi_bs_call(uninstall_multiple_protocol_interfaces, - zboot_handle, - &EFI_DEVICE_PATH_PROTOCOL_GUID, lf2_dp, - &EFI_LOAD_FILE2_PROTOCOL_GUID, &zboot_load_file2, - NULL); - -free_dpalloc: - efi_bs_call(free_pool, dp_alloc); + efi_cache_sync_image(image_base, alloc_size, code_size); - efi_bs_call(exit, handle, status, exit_data_size, exit_data); + status = efi_stub_common(handle, image, image_base, cmdline_ptr); - // Free ExitData in case Exit() returned with a failure code, - // but return the original status code. - error("Exit() returned with failure code"); - if (exit_data != NULL) - efi_bs_call(free_pool, exit_data); +free_image: + efi_free(alloc_size, image_base); +free_cmdline: + efi_bs_call(free_pool, cmdline_ptr); return status; } diff --git a/include/linux/efi.h b/include/linux/efi.h index dfa6cc8bbef9..2ca082e3f136 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -411,7 +411,6 @@ 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_ZBOOT_MEDIA_GUID EFI_GUID(0xe565a30d, 0x47da, 0x4dbd, 0xb3, 0x54, 0x9b, 0xb5, 0xc8, 0x4f, 0x8b, 0xe2) #define LINUX_EFI_MOK_VARIABLE_TABLE_GUID EFI_GUID(0xc451ed2b, 0x9694, 0x45d3, 0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89) #define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47) #define LINUX_EFI_BOOT_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) -- cgit v1.2.3 From 9cf42bca30e98a1c6c9e8abf876940a551eaa3d1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 2 Aug 2022 11:00:16 +0200 Subject: efi: libstub: use EFI_LOADER_CODE region when moving the kernel in memory The EFI spec is not very clear about which permissions are being given when allocating pages of a certain type. However, it is quite obvious that EFI_LOADER_CODE is more likely to permit execution than EFI_LOADER_DATA, which becomes relevant once we permit booting the kernel proper with the firmware's 1:1 mapping still active. Ostensibly, recent systems such as the Surface Pro X grant executable permissions to EFI_LOADER_CODE regions but not EFI_LOADER_DATA regions. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/alignedmem.c | 5 +++-- drivers/firmware/efi/libstub/arm64-stub.c | 6 ++++-- drivers/firmware/efi/libstub/efistub.h | 6 ++++-- drivers/firmware/efi/libstub/mem.c | 3 ++- drivers/firmware/efi/libstub/randomalloc.c | 5 +++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c index 1de9878ddd3a..174832661251 100644 --- a/drivers/firmware/efi/libstub/alignedmem.c +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -22,7 +22,8 @@ * Return: status code */ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, - unsigned long max, unsigned long align) + unsigned long max, unsigned long align, + int memory_type) { efi_physical_addr_t alloc_addr; efi_status_t status; @@ -36,7 +37,7 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, slack = align / EFI_PAGE_SIZE - 1; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, - EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + memory_type, size / EFI_PAGE_SIZE + slack, &alloc_addr); if (status != EFI_SUCCESS) return status; diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index c14028b625b4..7f0aab3a8ab3 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -102,7 +102,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, * locate the kernel at a randomized offset in physical memory. */ status = efi_random_alloc(*reserve_size, min_kimg_align, - reserve_addr, phys_seed); + reserve_addr, phys_seed, + EFI_LOADER_CODE); if (status != EFI_SUCCESS) efi_warn("efi_random_alloc() failed: 0x%lx\n", status); } else { @@ -123,7 +124,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, } status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, - ULONG_MAX, min_kimg_align); + ULONG_MAX, min_kimg_align, + EFI_LOADER_CODE); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 21b01d6c77ba..e85916ed5311 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -880,7 +880,8 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); efi_status_t efi_random_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long random_seed); + unsigned long *addr, unsigned long random_seed, + int memory_type); efi_status_t check_platform_features(void); @@ -905,7 +906,8 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, unsigned long max); efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, - unsigned long max, unsigned long align); + unsigned long max, unsigned long align, + int memory_type); efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, unsigned long *addr, unsigned long min); diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 45841ef55a9f..03d147f17185 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -91,7 +91,8 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE) return efi_allocate_pages_aligned(size, addr, max, - EFI_ALLOC_ALIGN); + EFI_ALLOC_ALIGN, + EFI_LOADER_DATA); alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index 9fb5869896be..ec44bb7e092f 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -53,7 +53,8 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, - unsigned long random_seed) + unsigned long random_seed, + int memory_type) { unsigned long total_slots = 0, target_slot; unsigned long total_mirrored_slots = 0; @@ -118,7 +119,7 @@ efi_status_t efi_random_alloc(unsigned long size, pages = size / EFI_PAGE_SIZE; status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, pages, &target); + memory_type, pages, &target); if (status == EFI_SUCCESS) *addr = target; break; -- cgit v1.2.3 From 70912985545adc81716164271401c9ffb0acdd0f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 26 Sep 2022 21:32:16 +0200 Subject: efi: libstub: Implement devicepath support for initrd commandline loader Currently, the initrd= command line option to the EFI stub only supports loading files that reside on the same volume as the loaded image, which is not workable for loaders like GRUB that don't even implement the volume abstraction (EFI_SIMPLE_FILE_SYSTEM_PROTOCOL), and load the kernel from an anonymous buffer in memory. For this reason, another method was devised that relies on the LoadFile2 protocol. However, the command line loader is rather useful when using the UEFI shell or other generic loaders that have no awareness of Linux specific protocols so let's make it a bit more flexible, by permitting textual device paths to be provided to initrd= as well, provided that they refer to a file hosted on a EFI_SIMPLE_FILE_SYSTEM_PROTOCOL volume. E.g., initrd=PciRoot(0x0)/Pci(0x3,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/rootfs.cpio.gz Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 15 +++++++ drivers/firmware/efi/libstub/file.c | 78 ++++++++++++++++++++++++++++------ include/linux/efi.h | 6 +++ 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index e85916ed5311..1fde9cbe6899 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -179,6 +179,21 @@ union efi_device_path_to_text_protocol { typedef union efi_device_path_to_text_protocol efi_device_path_to_text_protocol_t; +union efi_device_path_from_text_protocol { + struct { + efi_device_path_protocol_t * + (__efiapi *convert_text_to_device_node)(const efi_char16_t *); + efi_device_path_protocol_t * + (__efiapi *convert_text_to_device_path)(const efi_char16_t *); + }; + struct { + u32 convert_text_to_device_node; + u32 convert_text_to_device_path; + } mixed_mode; +}; + +typedef union efi_device_path_from_text_protocol efi_device_path_from_text_protocol_t; + typedef void *efi_event_t; /* Note that notifications won't work in mixed mode */ typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *); diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 246ccc5b015d..20d3530ca9c5 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -43,6 +43,13 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, efi_file_protocol_t *fh; unsigned long info_sz; efi_status_t status; + efi_char16_t *c; + + /* Replace UNIX dir separators with EFI standard ones */ + for (c = fi->filename; *c != L'\0'; c++) { + if (*c == L'/') + *c = L'\\'; + } status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { @@ -111,16 +118,61 @@ static int find_file_option(const efi_char16_t *cmdline, int cmdline_len, if (c == L'\0' || c == L'\n' || c == L' ') break; - else if (c == L'/') - /* Replace UNIX dir separators with EFI standard ones */ - *result++ = L'\\'; - else - *result++ = c; + *result++ = c; } *result = L'\0'; return i; } +static efi_status_t efi_open_device_path(efi_file_protocol_t **volume, + struct finfo *fi) +{ + efi_guid_t text_to_dp_guid = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; + static efi_device_path_from_text_protocol_t *text_to_dp = NULL; + efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; + efi_device_path_protocol_t *initrd_dp; + efi_simple_file_system_protocol_t *io; + struct efi_file_path_dev_path *fpath; + efi_handle_t handle; + efi_status_t status; + + /* See if the text to device path protocol exists */ + if (!text_to_dp && + efi_bs_call(locate_protocol, &text_to_dp_guid, NULL, + (void **)&text_to_dp) != EFI_SUCCESS) + return EFI_UNSUPPORTED; + + + /* Convert the filename wide string into a device path */ + initrd_dp = text_to_dp->convert_text_to_device_path(fi->filename); + + /* Check whether the device path in question implements simple FS */ + if ((efi_bs_call(locate_device_path, &fs_proto, &initrd_dp, &handle) ?: + efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io)) + != EFI_SUCCESS) + return EFI_NOT_FOUND; + + /* Check whether the remaining device path is a file device path */ + if (initrd_dp->type != EFI_DEV_MEDIA || + initrd_dp->sub_type != EFI_DEV_MEDIA_FILE) { + efi_warn("Unexpected device path node type: (%x, %x)\n", + initrd_dp->type, initrd_dp->sub_type); + return EFI_LOAD_ERROR; + } + + /* Copy the remaining file path into the fi structure */ + fpath = (struct efi_file_path_dev_path *)initrd_dp; + memcpy(fi->filename, fpath->filename, + min(sizeof(fi->filename), + fpath->header.length - sizeof(fpath->header))); + + status = io->open_volume(io, volume); + if (status != EFI_SUCCESS) + efi_err("Failed to open volume\n"); + + return status; +} + /* * Check the cmdline for a LILO-style file= arguments. * @@ -170,11 +222,13 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, cmdline += offset; cmdline_len -= offset; - if (!volume) { + status = efi_open_device_path(&volume, &fi); + if (status == EFI_UNSUPPORTED || status == EFI_NOT_FOUND) + /* try the volume that holds the kernel itself */ status = efi_open_volume(image, &volume); - if (status != EFI_SUCCESS) - return status; - } + + if (status != EFI_SUCCESS) + goto err_free_alloc; status = efi_open_file(volume, &fi, &file, &size); if (status != EFI_SUCCESS) @@ -231,14 +285,12 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, size -= chunksize; } file->close(file); + volume->close(volume); } while (offset > 0); *load_addr = alloc_addr; *load_size = alloc_size; - if (volume) - volume->close(volume); - if (*load_size == 0) return EFI_NOT_READY; return EFI_SUCCESS; @@ -248,6 +300,8 @@ err_close_file: err_close_volume: volume->close(volume); + +err_free_alloc: efi_free(alloc_size, alloc_addr); return status; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 2d4d5a7fd781..ee034f0c5cc2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -371,6 +371,7 @@ void efi_native_runtime_setup(void); #define LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID EFI_GUID(0xbc62157e, 0x3e33, 0x4fec, 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf) #define EFI_DEVICE_PATH_PROTOCOL_GUID EFI_GUID(0x09576e91, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID EFI_GUID(0x8b843e20, 0x8132, 0x4852, 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c) +#define EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID EFI_GUID(0x05c99a21, 0xc70f, 0x4ad2, 0x8a, 0x5f, 0x35, 0xdf, 0x33, 0x43, 0xf5, 0x1e) #define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a) #define EFI_UGA_PROTOCOL_GUID EFI_GUID(0x982c298b, 0xf4fa, 0x41cb, 0xb8, 0x38, 0x77, 0xaa, 0x68, 0x8f, 0xb8, 0x39) #define EFI_PCI_IO_PROTOCOL_GUID EFI_GUID(0x4cf5b200, 0x68b8, 0x4ca5, 0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x02, 0x9a) @@ -1011,6 +1012,11 @@ struct efi_mem_mapped_dev_path { u64 ending_addr; } __packed; +struct efi_file_path_dev_path { + struct efi_generic_dev_path header; + efi_char16_t filename[]; +} __packed; + struct efi_dev_path { union { struct efi_generic_dev_path header; -- cgit v1.2.3 From a61962d8e7d3f11f68cbaebd477fbd8e1f7b9f6d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 26 Sep 2022 21:14:23 +0200 Subject: efi: libstub: Permit mixed mode return types other than efi_status_t Rework the EFI stub macro wrappers around protocol method calls and other indirect calls in order to allow return types other than efi_status_t. This means the widening should be conditional on whether or not the return type is efi_status_t, and should be omitted otherwise. Also, switch to _Generic() to implement the type based compile time conditionals, which is more concise, and distinguishes between efi_status_t and u64 properly. Signed-off-by: Ard Biesheuvel --- arch/x86/boot/compressed/efi_thunk_64.S | 6 --- arch/x86/include/asm/efi.h | 65 +++++++++++++++------------------ drivers/firmware/efi/libstub/efistub.h | 20 +++++++--- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/arch/x86/boot/compressed/efi_thunk_64.S b/arch/x86/boot/compressed/efi_thunk_64.S index 67e7edcdfea8..0c988f2a1243 100644 --- a/arch/x86/boot/compressed/efi_thunk_64.S +++ b/arch/x86/boot/compressed/efi_thunk_64.S @@ -93,12 +93,6 @@ SYM_FUNC_START(__efi64_thunk) movl %ebx, %fs movl %ebx, %gs - /* - * Convert 32-bit status code into 64-bit. - */ - roll $1, %eax - rorq $1, %rax - pop %rbx pop %rbp RET diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 233ae6986d6f..f76f15f634bc 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -178,7 +178,7 @@ struct efi_setup_data { extern u64 efi_setup; #ifdef CONFIG_EFI -extern efi_status_t __efi64_thunk(u32, ...); +extern u64 __efi64_thunk(u32, ...); #define efi64_thunk(...) ({ \ u64 __pad[3]; /* must have space for 3 args on the stack */ \ @@ -228,16 +228,15 @@ static inline bool efi_is_native(void) return efi_is_64bit(); } -#define efi_mixed_mode_cast(attr) \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(u32, __typeof__(attr)), \ - (unsigned long)(attr), (attr)) - #define efi_table_attr(inst, attr) \ - (efi_is_native() \ - ? inst->attr \ - : (__typeof__(inst->attr)) \ - efi_mixed_mode_cast(inst->mixed_mode.attr)) + (efi_is_native() ? (inst)->attr \ + : efi_mixed_table_attr((inst), attr)) + +#define efi_mixed_table_attr(inst, attr) \ + (__typeof__(inst->attr)) \ + _Generic(inst->mixed_mode.attr, \ + u32: (unsigned long)(inst->mixed_mode.attr), \ + default: (inst->mixed_mode.attr)) /* * The following macros allow translating arguments if necessary from native to @@ -344,31 +343,27 @@ static inline u32 efi64_convert_status(efi_status_t status) #define __efi_eat(...) #define __efi_eval(...) __VA_ARGS__ -/* The three macros below handle dispatching via the thunk if needed */ - -#define efi_call_proto(inst, func, ...) \ - (efi_is_native() \ - ? inst->func(inst, ##__VA_ARGS__) \ - : __efi64_thunk_map(inst, func, inst, ##__VA_ARGS__)) - -#define efi_bs_call(func, ...) \ - (efi_is_native() \ - ? efi_system_table->boottime->func(__VA_ARGS__) \ - : __efi64_thunk_map(efi_table_attr(efi_system_table, \ - boottime), \ - func, __VA_ARGS__)) - -#define efi_rt_call(func, ...) \ - (efi_is_native() \ - ? efi_system_table->runtime->func(__VA_ARGS__) \ - : __efi64_thunk_map(efi_table_attr(efi_system_table, \ - runtime), \ - func, __VA_ARGS__)) - -#define efi_dxe_call(func, ...) \ - (efi_is_native() \ - ? efi_dxe_table->func(__VA_ARGS__) \ - : __efi64_thunk_map(efi_dxe_table, func, __VA_ARGS__)) +static inline efi_status_t __efi64_widen_efi_status(u64 status) +{ + /* use rotate to move the value of bit #31 into position #63 */ + return ror64(rol32(status, 1), 1); +} + +/* The macro below handles dispatching via the thunk if needed */ + +#define efi_fn_call(inst, func, ...) \ + (efi_is_native() ? (inst)->func(__VA_ARGS__) \ + : efi_mixed_call((inst), func, ##__VA_ARGS__)) + +#define efi_mixed_call(inst, func, ...) \ + _Generic(inst->func(__VA_ARGS__), \ + efi_status_t: \ + __efi64_widen_efi_status( \ + __efi64_thunk_map(inst, func, ##__VA_ARGS__)), \ + u64: ({ BUILD_BUG(); ULONG_MAX; }), \ + default: \ + (__typeof__(inst->func(__VA_ARGS__))) \ + __efi64_thunk_map(inst, func, ##__VA_ARGS__)) #else /* CONFIG_EFI_MIXED */ diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 1fde9cbe6899..8bb11fb309c3 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -44,15 +44,23 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, #ifndef ARCH_HAS_EFISTUB_WRAPPERS -#define efi_is_native() (true) -#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__) -#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__) -#define efi_dxe_call(func, ...) efi_dxe_table->func(__VA_ARGS__) -#define efi_table_attr(inst, attr) (inst->attr) -#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__) +#define efi_is_native() (true) +#define efi_table_attr(inst, attr) (inst)->attr +#define efi_fn_call(inst, func, ...) (inst)->func(__VA_ARGS__) #endif +#define efi_call_proto(inst, func, ...) ({ \ + __typeof__(inst) __inst = (inst); \ + efi_fn_call(__inst, func, __inst, ##__VA_ARGS__); \ +}) +#define efi_bs_call(func, ...) \ + efi_fn_call(efi_table_attr(efi_system_table, boottime), func, ##__VA_ARGS__) +#define efi_rt_call(func, ...) \ + efi_fn_call(efi_table_attr(efi_system_table, runtime), func, ##__VA_ARGS__) +#define efi_dxe_call(func, ...) \ + efi_fn_call(efi_dxe_table, func, ##__VA_ARGS__) + #define efi_info(fmt, ...) \ efi_printk(KERN_INFO fmt, ##__VA_ARGS__) #define efi_warn(fmt, ...) \ -- cgit v1.2.3 From f8a31244d73288bd623a0247512f6c0b81844a92 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 26 Sep 2022 22:17:21 +0200 Subject: efi: libstub: Add mixed mode support to command line initrd loader Now that we have support for calling protocols that need additional marshalling for mixed mode, wire up the initrd command line loader. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 11 ++++ drivers/firmware/efi/libstub/efi-stub-helper.c | 2 +- drivers/firmware/efi/libstub/efistub.h | 81 +++++++++++++++++--------- drivers/firmware/efi/libstub/file.c | 32 +++++----- 4 files changed, 83 insertions(+), 43 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index f76f15f634bc..aaed31e640e5 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -324,6 +324,17 @@ static inline u32 efi64_convert_status(efi_status_t status) #define __efi64_argmap_set_memory_space_attributes(phys, size, flags) \ (__efi64_split(phys), __efi64_split(size), __efi64_split(flags)) +/* file protocol */ +#define __efi64_argmap_open(prot, newh, fname, mode, attr) \ + ((prot), efi64_zero_upper(newh), (fname), __efi64_split(mode), \ + __efi64_split(attr)) + +#define __efi64_argmap_set_position(pos) (__efi64_split(pos)) + +/* file system protocol */ +#define __efi64_argmap_open_volume(prot, file) \ + ((prot), efi64_zero_upper(file)) + /* * The macros below handle the plumbing for the argument mapping. To add a * mapping for a specific EFI method, simply define a macro diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 2184f3f153ef..01680b11d46d 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -539,7 +539,7 @@ efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, unsigned long hard_limit) { if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || - (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) + (IS_ENABLED(CONFIG_X86) && image == NULL)) return EFI_UNSUPPORTED; return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 8bb11fb309c3..5615ad6f2958 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -595,36 +595,63 @@ typedef struct { efi_char16_t filename[]; } efi_file_info_t; -typedef struct efi_file_protocol efi_file_protocol_t; - -struct efi_file_protocol { - u64 revision; - efi_status_t (__efiapi *open) (efi_file_protocol_t *, - efi_file_protocol_t **, - efi_char16_t *, u64, u64); - efi_status_t (__efiapi *close) (efi_file_protocol_t *); - efi_status_t (__efiapi *delete) (efi_file_protocol_t *); - efi_status_t (__efiapi *read) (efi_file_protocol_t *, - unsigned long *, void *); - efi_status_t (__efiapi *write) (efi_file_protocol_t *, - unsigned long, void *); - efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, u64 *); - efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, u64); - efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, - efi_guid_t *, unsigned long *, - void *); - efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, - efi_guid_t *, unsigned long, - void *); - efi_status_t (__efiapi *flush) (efi_file_protocol_t *); +typedef union efi_file_protocol efi_file_protocol_t; + +union efi_file_protocol { + struct { + u64 revision; + efi_status_t (__efiapi *open) (efi_file_protocol_t *, + efi_file_protocol_t **, + efi_char16_t *, u64, + u64); + efi_status_t (__efiapi *close) (efi_file_protocol_t *); + efi_status_t (__efiapi *delete) (efi_file_protocol_t *); + efi_status_t (__efiapi *read) (efi_file_protocol_t *, + unsigned long *, + void *); + efi_status_t (__efiapi *write) (efi_file_protocol_t *, + unsigned long, void *); + efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, + u64 *); + efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, + u64); + efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, + efi_guid_t *, + unsigned long *, + void *); + efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, + efi_guid_t *, + unsigned long, + void *); + efi_status_t (__efiapi *flush) (efi_file_protocol_t *); + }; + struct { + u64 revision; + u32 open; + u32 close; + u32 delete; + u32 read; + u32 write; + u32 get_position; + u32 set_position; + u32 get_info; + u32 set_info; + u32 flush; + } mixed_mode; }; -typedef struct efi_simple_file_system_protocol efi_simple_file_system_protocol_t; +typedef union efi_simple_file_system_protocol efi_simple_file_system_protocol_t; -struct efi_simple_file_system_protocol { - u64 revision; - int (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, - efi_file_protocol_t **); +union efi_simple_file_system_protocol { + struct { + u64 revision; + efi_status_t (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, + efi_file_protocol_t **); + }; + struct { + u64 revision; + u32 open_volume; + } mixed_mode; }; #define EFI_FILE_MODE_READ 0x0000000000000001 diff --git a/drivers/firmware/efi/libstub/file.c b/drivers/firmware/efi/libstub/file.c index 20d3530ca9c5..d6a025df07dc 100644 --- a/drivers/firmware/efi/libstub/file.c +++ b/drivers/firmware/efi/libstub/file.c @@ -51,17 +51,18 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume, *c = L'\\'; } - status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0); + status = efi_call_proto(volume, open, &fh, fi->filename, + EFI_FILE_MODE_READ, 0); if (status != EFI_SUCCESS) { efi_err("Failed to open file: %ls\n", fi->filename); return status; } info_sz = sizeof(struct finfo); - status = fh->get_info(fh, &info_guid, &info_sz, fi); + status = efi_call_proto(fh, get_info, &info_guid, &info_sz, fi); if (status != EFI_SUCCESS) { efi_err("Failed to get file info\n"); - fh->close(fh); + efi_call_proto(fh, close); return status; } @@ -77,14 +78,14 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image, efi_simple_file_system_protocol_t *io; efi_status_t status; - status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto, - (void **)&io); + status = efi_bs_call(handle_protocol, efi_table_attr(image, device_handle), + &fs_proto, (void **)&io); if (status != EFI_SUCCESS) { efi_err("Failed to handle fs_proto\n"); return status; } - status = io->open_volume(io, fh); + status = efi_call_proto(io, open_volume, fh); if (status != EFI_SUCCESS) efi_err("Failed to open volume\n"); @@ -144,7 +145,8 @@ static efi_status_t efi_open_device_path(efi_file_protocol_t **volume, /* Convert the filename wide string into a device path */ - initrd_dp = text_to_dp->convert_text_to_device_path(fi->filename); + initrd_dp = efi_fn_call(text_to_dp, convert_text_to_device_path, + fi->filename); /* Check whether the device path in question implements simple FS */ if ((efi_bs_call(locate_device_path, &fs_proto, &initrd_dp, &handle) ?: @@ -166,7 +168,7 @@ static efi_status_t efi_open_device_path(efi_file_protocol_t **volume, min(sizeof(fi->filename), fpath->header.length - sizeof(fpath->header))); - status = io->open_volume(io, volume); + status = efi_call_proto(io, open_volume, volume); if (status != EFI_SUCCESS) efi_err("Failed to open volume\n"); @@ -187,8 +189,8 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, unsigned long *load_addr, unsigned long *load_size) { - const efi_char16_t *cmdline = image->load_options; - u32 cmdline_len = image->load_options_size; + const efi_char16_t *cmdline = efi_table_attr(image, load_options); + u32 cmdline_len = efi_table_attr(image, load_options_size); unsigned long efi_chunk_size = ULONG_MAX; efi_file_protocol_t *volume = NULL; efi_file_protocol_t *file; @@ -276,7 +278,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, while (size) { unsigned long chunksize = min(size, efi_chunk_size); - status = file->read(file, &chunksize, addr); + status = efi_call_proto(file, read, &chunksize, addr); if (status != EFI_SUCCESS) { efi_err("Failed to read file\n"); goto err_close_file; @@ -284,8 +286,8 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, addr += chunksize; size -= chunksize; } - file->close(file); - volume->close(volume); + efi_call_proto(file, close); + efi_call_proto(volume, close); } while (offset > 0); *load_addr = alloc_addr; @@ -296,10 +298,10 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, return EFI_SUCCESS; err_close_file: - file->close(file); + efi_call_proto(file, close); err_close_volume: - volume->close(volume); + efi_call_proto(volume, close); err_free_alloc: efi_free(alloc_size, alloc_addr); -- cgit v1.2.3 From 75e1a2460d79566bf1e43e5a3a7a9039510a82e0 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 26 Sep 2022 23:23:04 +0200 Subject: efi: libstub: Undeprecate the command line initrd loader The initrd= command line loader can be useful for development, but it was limited to loading files from the same file system as the loaded kernel (and it didn't work on x86 mixed mode). As both issues have been fixed, and the initrd= can now be used with files residing on any simple file system exposed by the EFI firmware, let's permit it to be enabled on RISC-V and LoongArch, which did not support it up to this point. Note that LoadFile2 remains the preferred option, as it is much simpler to use and implement, but generic loaders (including the UEFI shell) may not implement this so there, initrd= can now be used as well (if enabled in the build) Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 6787ed8dfacf..c666ea4f1252 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -143,13 +143,16 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER bool "Enable the command line initrd loader" if !X86 depends on EFI_STUB && (EFI_GENERIC_STUB || X86) default y if X86 - depends on !RISCV && !LOONGARCH help Select this config option to add support for the initrd= command - line parameter, allowing an initrd that resides on the same volume - as the kernel image to be loaded into memory. - - This method is deprecated. + line parameter, allowing an initrd to be loaded into memory that + resides on a file system backed by an implementation of + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. + + This method has been superseded by the simpler LoadFile2 based + initrd loading method, but the initrd= loader is retained as it + can be used from the UEFI Shell or other generic loaders that + don't implement the Linux specific LoadFile2 method. config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" -- cgit v1.2.3 From 4059ba656ce5c13c8ca345955712152ae41420c8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 1 Oct 2022 18:38:15 +0200 Subject: efi: memmap: Move EFI fake memmap support into x86 arch tree The EFI fake memmap support is specific to x86, which manipulates the EFI memory map in various different ways after receiving it from the EFI stub. On other architectures, we have managed to push back on this, and the EFI memory map is kept pristine. So let's move the fake memmap code into the x86 arch tree, where it arguably belongs. Signed-off-by: Ard Biesheuvel --- arch/x86/Kconfig | 20 ++++ arch/x86/include/asm/efi.h | 5 + arch/x86/kernel/setup.c | 1 + arch/x86/platform/efi/Makefile | 1 + arch/x86/platform/efi/fake_mem.c | 197 ++++++++++++++++++++++++++++++++++++ drivers/firmware/efi/Kconfig | 22 ---- drivers/firmware/efi/Makefile | 4 - drivers/firmware/efi/fake_mem.c | 124 ----------------------- drivers/firmware/efi/fake_mem.h | 10 -- drivers/firmware/efi/x86_fake_mem.c | 75 -------------- include/linux/efi.h | 6 -- 11 files changed, 224 insertions(+), 241 deletions(-) create mode 100644 arch/x86/platform/efi/fake_mem.c delete mode 100644 drivers/firmware/efi/fake_mem.c delete mode 100644 drivers/firmware/efi/fake_mem.h delete mode 100644 drivers/firmware/efi/x86_fake_mem.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 67745ceab0db..e2f89a7495d2 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1994,6 +1994,26 @@ config EFI_MIXED If unsure, say N. +config EFI_FAKE_MEMMAP + bool "Enable EFI fake memory map" + depends on EFI + help + Saying Y here will enable "efi_fake_mem" boot option. By specifying + this parameter, you can add arbitrary attribute to specific memory + range by updating original (firmware provided) EFI memmap. This is + useful for debugging of EFI memmap related feature, e.g., Address + Range Mirroring feature. + +config EFI_MAX_FAKE_MEM + int "maximum allowable number of ranges in efi_fake_mem boot option" + depends on EFI_FAKE_MEMMAP + range 1 128 + default 8 + help + Maximum allowable number of ranges in efi_fake_mem boot option. + Ranges can be set up to this value using comma-separated list. + The default value is 8. + source "kernel/Kconfig.hz" config KEXEC diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index aaed31e640e5..981eaf535703 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -406,10 +406,15 @@ static inline void efi_reserve_boot_services(void) #ifdef CONFIG_EFI_FAKE_MEMMAP extern void __init efi_fake_memmap_early(void); +extern void __init efi_fake_memmap(void); #else static inline void efi_fake_memmap_early(void) { } + +static inline void efi_fake_memmap(void) +{ +} #endif #define arch_ima_efi_boot_mode \ diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 216fee7144ee..41ec3a69f3c7 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index a50245157685..b481719b16cc 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -4,3 +4,4 @@ GCOV_PROFILE := n obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o +obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o diff --git a/arch/x86/platform/efi/fake_mem.c b/arch/x86/platform/efi/fake_mem.c new file mode 100644 index 000000000000..41d57cad3d84 --- /dev/null +++ b/arch/x86/platform/efi/fake_mem.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fake_mem.c + * + * Copyright (C) 2015 FUJITSU LIMITED + * Author: Taku Izumi + * + * This code introduces new boot option named "efi_fake_mem" + * By specifying this parameter, you can add arbitrary attribute to + * specific memory range by updating original (firmware provided) EFI + * memmap. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM + +static struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; +static int nr_fake_mem; + +static int __init cmp_fake_mem(const void *x1, const void *x2) +{ + const struct efi_mem_range *m1 = x1; + const struct efi_mem_range *m2 = x2; + + if (m1->range.start < m2->range.start) + return -1; + if (m1->range.start > m2->range.start) + return 1; + return 0; +} + +static void __init efi_fake_range(struct efi_mem_range *efi_range) +{ + struct efi_memory_map_data data = { 0 }; + int new_nr_map = efi.memmap.nr_map; + efi_memory_desc_t *md; + void *new_memmap; + + /* count up the number of EFI memory descriptor */ + for_each_efi_memory_desc(md) + new_nr_map += efi_memmap_split_count(md, &efi_range->range); + + /* allocate memory for new EFI memmap */ + if (efi_memmap_alloc(new_nr_map, &data) != 0) + return; + + /* create new EFI memmap */ + new_memmap = early_memremap(data.phys_map, data.size); + if (!new_memmap) { + __efi_memmap_free(data.phys_map, data.size, data.flags); + return; + } + + efi_memmap_insert(&efi.memmap, new_memmap, efi_range); + + /* swap into new EFI memmap */ + early_memunmap(new_memmap, data.size); + + efi_memmap_install(&data); +} + +void __init efi_fake_memmap(void) +{ + int i; + + if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) + return; + + for (i = 0; i < nr_fake_mem; i++) + efi_fake_range(&efi_fake_mems[i]); + + /* print new EFI memmap */ + efi_print_memmap(); +} + +static int __init setup_fake_mem(char *p) +{ + u64 start = 0, mem_size = 0, attribute = 0; + int i; + + if (!p) + return -EINVAL; + + while (*p != '\0') { + mem_size = memparse(p, &p); + if (*p == '@') + start = memparse(p+1, &p); + else + break; + + if (*p == ':') + attribute = simple_strtoull(p+1, &p, 0); + else + break; + + if (nr_fake_mem >= EFI_MAX_FAKEMEM) + break; + + efi_fake_mems[nr_fake_mem].range.start = start; + efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; + efi_fake_mems[nr_fake_mem].attribute = attribute; + nr_fake_mem++; + + if (*p == ',') + p++; + } + + sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), + cmp_fake_mem, NULL); + + for (i = 0; i < nr_fake_mem; i++) + pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", + efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, + efi_fake_mems[i].range.end); + + return *p == '\0' ? 0 : -EINVAL; +} + +early_param("efi_fake_mem", setup_fake_mem); + +void __init efi_fake_memmap_early(void) +{ + int i; + + /* + * The late efi_fake_mem() call can handle all requests if + * EFI_MEMORY_SP support is disabled. + */ + if (!efi_soft_reserve_enabled()) + return; + + if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) + return; + + /* + * Given that efi_fake_memmap() needs to perform memblock + * allocations it needs to run after e820__memblock_setup(). + * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given + * address range that potentially needs to mark the memory as + * reserved prior to e820__memblock_setup(). Update e820 + * directly if EFI_MEMORY_SP is specified for an + * EFI_CONVENTIONAL_MEMORY descriptor. + */ + for (i = 0; i < nr_fake_mem; i++) { + struct efi_mem_range *mem = &efi_fake_mems[i]; + efi_memory_desc_t *md; + u64 m_start, m_end; + + if ((mem->attribute & EFI_MEMORY_SP) == 0) + continue; + + m_start = mem->range.start; + m_end = mem->range.end; + for_each_efi_memory_desc(md) { + u64 start, end, size; + + if (md->type != EFI_CONVENTIONAL_MEMORY) + continue; + + start = md->phys_addr; + end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + if (m_start <= end && m_end >= start) + /* fake range overlaps descriptor */; + else + continue; + + /* + * Trim the boundary of the e820 update to the + * descriptor in case the fake range overlaps + * !EFI_CONVENTIONAL_MEMORY + */ + start = max(start, m_start); + end = min(end, m_end); + size = end - start + 1; + + if (end <= start) + continue; + + /* + * Ensure each efi_fake_mem instance results in + * a unique e820 resource + */ + e820__range_remove(start, size, E820_TYPE_RAM, 1); + e820__range_add(start, size, E820_TYPE_SOFT_RESERVED); + e820__update_table(e820_table); + } + } +} diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index c666ea4f1252..0d5201e49841 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -37,28 +37,6 @@ config EFI_RUNTIME_MAP See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. -config EFI_FAKE_MEMMAP - bool "Enable EFI fake memory map" - depends on EFI && X86 - default n - help - Saying Y here will enable "efi_fake_mem" boot option. - By specifying this parameter, you can add arbitrary attribute - to specific memory range by updating original (firmware provided) - EFI memmap. - This is useful for debugging of EFI memmap related feature. - e.g. Address Range Mirroring feature. - -config EFI_MAX_FAKE_MEM - int "maximum allowable number of ranges in efi_fake_mem boot option" - depends on EFI_FAKE_MEMMAP - range 1 128 - default 8 - help - Maximum allowable number of ranges in efi_fake_mem boot option. - Ranges can be set up to this value using comma-separated list. - The default value is 8. - config EFI_SOFT_RESERVE bool "Reserve EFI Specific Purpose Memory" depends on EFI && EFI_STUB && ACPI_HMAT diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 8d151e332584..8e4f0d5b26e5 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o subdir-$(CONFIG_EFI_STUB) += libstub -obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o @@ -32,9 +31,6 @@ 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 - obj-$(CONFIG_SYSFB) += sysfb_efi.o arm-obj-$(CONFIG_EFI) := efi-init.o arm-runtime.o diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c deleted file mode 100644 index 6e0f34a38171..000000000000 --- a/drivers/firmware/efi/fake_mem.c +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * fake_mem.c - * - * Copyright (C) 2015 FUJITSU LIMITED - * Author: Taku Izumi - * - * This code introduces new boot option named "efi_fake_mem" - * By specifying this parameter, you can add arbitrary attribute to - * specific memory range by updating original (firmware provided) EFI - * memmap. - */ - -#include -#include -#include -#include -#include -#include -#include "fake_mem.h" - -struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; -int nr_fake_mem; - -static int __init cmp_fake_mem(const void *x1, const void *x2) -{ - const struct efi_mem_range *m1 = x1; - const struct efi_mem_range *m2 = x2; - - if (m1->range.start < m2->range.start) - return -1; - if (m1->range.start > m2->range.start) - return 1; - return 0; -} - -static void __init efi_fake_range(struct efi_mem_range *efi_range) -{ - struct efi_memory_map_data data = { 0 }; - int new_nr_map = efi.memmap.nr_map; - efi_memory_desc_t *md; - void *new_memmap; - - /* count up the number of EFI memory descriptor */ - for_each_efi_memory_desc(md) - new_nr_map += efi_memmap_split_count(md, &efi_range->range); - - /* allocate memory for new EFI memmap */ - if (efi_memmap_alloc(new_nr_map, &data) != 0) - return; - - /* create new EFI memmap */ - new_memmap = early_memremap(data.phys_map, data.size); - if (!new_memmap) { - __efi_memmap_free(data.phys_map, data.size, data.flags); - return; - } - - efi_memmap_insert(&efi.memmap, new_memmap, efi_range); - - /* swap into new EFI memmap */ - early_memunmap(new_memmap, data.size); - - efi_memmap_install(&data); -} - -void __init efi_fake_memmap(void) -{ - int i; - - if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) - return; - - for (i = 0; i < nr_fake_mem; i++) - efi_fake_range(&efi_fake_mems[i]); - - /* print new EFI memmap */ - efi_print_memmap(); -} - -static int __init setup_fake_mem(char *p) -{ - u64 start = 0, mem_size = 0, attribute = 0; - int i; - - if (!p) - return -EINVAL; - - while (*p != '\0') { - mem_size = memparse(p, &p); - if (*p == '@') - start = memparse(p+1, &p); - else - break; - - if (*p == ':') - attribute = simple_strtoull(p+1, &p, 0); - else - break; - - if (nr_fake_mem >= EFI_MAX_FAKEMEM) - break; - - efi_fake_mems[nr_fake_mem].range.start = start; - efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; - efi_fake_mems[nr_fake_mem].attribute = attribute; - nr_fake_mem++; - - if (*p == ',') - p++; - } - - sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), - cmp_fake_mem, NULL); - - for (i = 0; i < nr_fake_mem; i++) - pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", - efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, - efi_fake_mems[i].range.end); - - return *p == '\0' ? 0 : -EINVAL; -} - -early_param("efi_fake_mem", setup_fake_mem); diff --git a/drivers/firmware/efi/fake_mem.h b/drivers/firmware/efi/fake_mem.h deleted file mode 100644 index d52791af4b18..000000000000 --- a/drivers/firmware/efi/fake_mem.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __EFI_FAKE_MEM_H__ -#define __EFI_FAKE_MEM_H__ -#include - -#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM - -extern struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; -extern int nr_fake_mem; -#endif /* __EFI_FAKE_MEM_H__ */ diff --git a/drivers/firmware/efi/x86_fake_mem.c b/drivers/firmware/efi/x86_fake_mem.c deleted file mode 100644 index 0bafcc1bb0f6..000000000000 --- a/drivers/firmware/efi/x86_fake_mem.c +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2019 Intel Corporation. All rights reserved. */ -#include -#include -#include "fake_mem.h" - -void __init efi_fake_memmap_early(void) -{ - int i; - - /* - * The late efi_fake_mem() call can handle all requests if - * EFI_MEMORY_SP support is disabled. - */ - if (!efi_soft_reserve_enabled()) - return; - - if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) - return; - - /* - * Given that efi_fake_memmap() needs to perform memblock - * allocations it needs to run after e820__memblock_setup(). - * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given - * address range that potentially needs to mark the memory as - * reserved prior to e820__memblock_setup(). Update e820 - * directly if EFI_MEMORY_SP is specified for an - * EFI_CONVENTIONAL_MEMORY descriptor. - */ - for (i = 0; i < nr_fake_mem; i++) { - struct efi_mem_range *mem = &efi_fake_mems[i]; - efi_memory_desc_t *md; - u64 m_start, m_end; - - if ((mem->attribute & EFI_MEMORY_SP) == 0) - continue; - - m_start = mem->range.start; - m_end = mem->range.end; - for_each_efi_memory_desc(md) { - u64 start, end, size; - - if (md->type != EFI_CONVENTIONAL_MEMORY) - continue; - - start = md->phys_addr; - end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - if (m_start <= end && m_end >= start) - /* fake range overlaps descriptor */; - else - continue; - - /* - * Trim the boundary of the e820 update to the - * descriptor in case the fake range overlaps - * !EFI_CONVENTIONAL_MEMORY - */ - start = max(start, m_start); - end = min(end, m_end); - size = end - start + 1; - - if (end <= start) - continue; - - /* - * Ensure each efi_fake_mem instance results in - * a unique e820 resource - */ - e820__range_remove(start, size, E820_TYPE_RAM, 1); - e820__range_add(start, size, E820_TYPE_SOFT_RESERVED); - e820__update_table(e820_table); - } - } -} diff --git a/include/linux/efi.h b/include/linux/efi.h index ee034f0c5cc2..43146530374e 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -749,12 +749,6 @@ extern struct kobject *efi_kobj; extern int efi_reboot_quirk_mode; extern bool efi_poweroff_required(void); -#ifdef CONFIG_EFI_FAKE_MEMMAP -extern void __init efi_fake_memmap(void); -#else -static inline void efi_fake_memmap(void) { } -#endif - extern unsigned long efi_mem_attr_table; /* -- cgit v1.2.3 From fdc6d38d64a20c542b1867ebeb8dd03b98829336 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 1 Oct 2022 19:09:24 +0200 Subject: efi: memmap: Move manipulation routines into x86 arch tree The EFI memory map is a description of the memory layout as provided by the firmware, and only x86 manipulates it in various different ways for its own memory bookkeeping. So let's move the memmap routines that are only used by x86 into the x86 arch tree. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 12 +++ arch/x86/platform/efi/Makefile | 3 +- arch/x86/platform/efi/memmap.c | 236 ++++++++++++++++++++++++++++++++++++++++ drivers/firmware/efi/memmap.c | 240 ++--------------------------------------- include/linux/efi.h | 10 +- 5 files changed, 261 insertions(+), 240 deletions(-) create mode 100644 arch/x86/platform/efi/memmap.c diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 981eaf535703..f29d1450edf4 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -417,6 +417,18 @@ static inline void efi_fake_memmap(void) } #endif +extern int __init efi_memmap_alloc(unsigned int num_entries, + struct efi_memory_map_data *data); +extern void __efi_memmap_free(u64 phys, unsigned long size, + unsigned long flags); +#define __efi_memmap_free __efi_memmap_free + +extern int __init efi_memmap_install(struct efi_memory_map_data *data); +extern int __init efi_memmap_split_count(efi_memory_desc_t *md, + struct range *range); +extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, + void *buf, struct efi_mem_range *mem); + #define arch_ima_efi_boot_mode \ ({ extern struct boot_params boot_params; boot_params.secure_boot; }) diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index b481719b16cc..ed5502a5185d 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -2,6 +2,7 @@ KASAN_SANITIZE := n GCOV_PROFILE := n -obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o +obj-$(CONFIG_EFI) += memmap.o quirks.o efi.o efi_$(BITS).o \ + efi_stub_$(BITS).o obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o diff --git a/arch/x86/platform/efi/memmap.c b/arch/x86/platform/efi/memmap.c new file mode 100644 index 000000000000..928e8c978859 --- /dev/null +++ b/arch/x86/platform/efi/memmap.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common EFI memory map functions. + */ + +#define pr_fmt(fmt) "efi: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) +{ + return memblock_phys_alloc(size, SMP_CACHE_BYTES); +} + +static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) +{ + unsigned int order = get_order(size); + struct page *p = alloc_pages(GFP_KERNEL, order); + + if (!p) + return 0; + + return PFN_PHYS(page_to_pfn(p)); +} + +void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) +{ + if (flags & EFI_MEMMAP_MEMBLOCK) { + if (slab_is_available()) + memblock_free_late(phys, size); + else + memblock_phys_free(phys, size); + } else if (flags & EFI_MEMMAP_SLAB) { + struct page *p = pfn_to_page(PHYS_PFN(phys)); + unsigned int order = get_order(size); + + free_pages((unsigned long) page_address(p), order); + } +} + +/** + * efi_memmap_alloc - Allocate memory for the EFI memory map + * @num_entries: Number of entries in the allocated map. + * @data: efi memmap installation parameters + * + * Depending on whether mm_init() has already been invoked or not, + * either memblock or "normal" page allocation is used. + * + * Returns zero on success, a negative error code on failure. + */ +int __init efi_memmap_alloc(unsigned int num_entries, + struct efi_memory_map_data *data) +{ + /* Expect allocation parameters are zero initialized */ + WARN_ON(data->phys_map || data->size); + + data->size = num_entries * efi.memmap.desc_size; + data->desc_version = efi.memmap.desc_version; + data->desc_size = efi.memmap.desc_size; + data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); + data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; + + if (slab_is_available()) { + data->flags |= EFI_MEMMAP_SLAB; + data->phys_map = __efi_memmap_alloc_late(data->size); + } else { + data->flags |= EFI_MEMMAP_MEMBLOCK; + data->phys_map = __efi_memmap_alloc_early(data->size); + } + + if (!data->phys_map) + return -ENOMEM; + return 0; +} + +/** + * efi_memmap_install - Install a new EFI memory map in efi.memmap + * @ctx: map allocation parameters (address, size, flags) + * + * Unlike efi_memmap_init_*(), this function does not allow the caller + * to switch from early to late mappings. It simply uses the existing + * mapping function and installs the new memmap. + * + * Returns zero on success, a negative error code on failure. + */ +int __init efi_memmap_install(struct efi_memory_map_data *data) +{ + efi_memmap_unmap(); + + return __efi_memmap_init(data); +} + +/** + * efi_memmap_split_count - Count number of additional EFI memmap entries + * @md: EFI memory descriptor to split + * @range: Address range (start, end) to split around + * + * Returns the number of additional EFI memmap entries required to + * accommodate @range. + */ +int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) +{ + u64 m_start, m_end; + u64 start, end; + int count = 0; + + start = md->phys_addr; + end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + /* modifying range */ + m_start = range->start; + m_end = range->end; + + if (m_start <= start) { + /* split into 2 parts */ + if (start < m_end && m_end < end) + count++; + } + + if (start < m_start && m_start < end) { + /* split into 3 parts */ + if (m_end < end) + count += 2; + /* split into 2 parts */ + if (end <= m_end) + count++; + } + + return count; +} + +/** + * efi_memmap_insert - Insert a memory region in an EFI memmap + * @old_memmap: The existing EFI memory map structure + * @buf: Address of buffer to store new map + * @mem: Memory map entry to insert + * + * It is suggested that you call efi_memmap_split_count() first + * to see how large @buf needs to be. + */ +void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, + struct efi_mem_range *mem) +{ + u64 m_start, m_end, m_attr; + efi_memory_desc_t *md; + u64 start, end; + void *old, *new; + + /* modifying range */ + m_start = mem->range.start; + m_end = mem->range.end; + m_attr = mem->attribute; + + /* + * The EFI memory map deals with regions in EFI_PAGE_SIZE + * units. Ensure that the region described by 'mem' is aligned + * correctly. + */ + if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || + !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { + WARN_ON(1); + return; + } + + for (old = old_memmap->map, new = buf; + old < old_memmap->map_end; + old += old_memmap->desc_size, new += old_memmap->desc_size) { + + /* copy original EFI memory descriptor */ + memcpy(new, old, old_memmap->desc_size); + md = new; + start = md->phys_addr; + end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + if (m_start <= start && end <= m_end) + md->attribute |= m_attr; + + if (m_start <= start && + (start < m_end && m_end < end)) { + /* first part */ + md->attribute |= m_attr; + md->num_pages = (m_end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + /* latter part */ + new += old_memmap->desc_size; + memcpy(new, old, old_memmap->desc_size); + md = new; + md->phys_addr = m_end + 1; + md->num_pages = (end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + } + + if ((start < m_start && m_start < end) && m_end < end) { + /* first part */ + md->num_pages = (m_start - md->phys_addr) >> + EFI_PAGE_SHIFT; + /* middle part */ + new += old_memmap->desc_size; + memcpy(new, old, old_memmap->desc_size); + md = new; + md->attribute |= m_attr; + md->phys_addr = m_start; + md->num_pages = (m_end - m_start + 1) >> + EFI_PAGE_SHIFT; + /* last part */ + new += old_memmap->desc_size; + memcpy(new, old, old_memmap->desc_size); + md = new; + md->phys_addr = m_end + 1; + md->num_pages = (end - m_end) >> + EFI_PAGE_SHIFT; + } + + if ((start < m_start && m_start < end) && + (end <= m_end)) { + /* first part */ + md->num_pages = (m_start - md->phys_addr) >> + EFI_PAGE_SHIFT; + /* latter part */ + new += old_memmap->desc_size; + memcpy(new, old, old_memmap->desc_size); + md = new; + md->phys_addr = m_start; + md->num_pages = (end - md->phys_addr + 1) >> + EFI_PAGE_SHIFT; + md->attribute |= m_attr; + } + } +} diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 6ec7970dbd40..e6256c48284e 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -9,82 +9,15 @@ #include #include #include -#include #include #include -static phys_addr_t __init __efi_memmap_alloc_early(unsigned long size) -{ - return memblock_phys_alloc(size, SMP_CACHE_BYTES); -} - -static phys_addr_t __init __efi_memmap_alloc_late(unsigned long size) -{ - unsigned int order = get_order(size); - struct page *p = alloc_pages(GFP_KERNEL, order); - - if (!p) - return 0; - - return PFN_PHYS(page_to_pfn(p)); -} - -void __init __efi_memmap_free(u64 phys, unsigned long size, unsigned long flags) -{ - if (flags & EFI_MEMMAP_MEMBLOCK) { - if (slab_is_available()) - memblock_free_late(phys, size); - else - memblock_phys_free(phys, size); - } else if (flags & EFI_MEMMAP_SLAB) { - struct page *p = pfn_to_page(PHYS_PFN(phys)); - unsigned int order = get_order(size); - - free_pages((unsigned long) page_address(p), order); - } -} - -static void __init efi_memmap_free(void) -{ - __efi_memmap_free(efi.memmap.phys_map, - efi.memmap.desc_size * efi.memmap.nr_map, - efi.memmap.flags); -} - -/** - * efi_memmap_alloc - Allocate memory for the EFI memory map - * @num_entries: Number of entries in the allocated map. - * @data: efi memmap installation parameters - * - * Depending on whether mm_init() has already been invoked or not, - * either memblock or "normal" page allocation is used. - * - * Returns zero on success, a negative error code on failure. - */ -int __init efi_memmap_alloc(unsigned int num_entries, - struct efi_memory_map_data *data) -{ - /* Expect allocation parameters are zero initialized */ - WARN_ON(data->phys_map || data->size); - - data->size = num_entries * efi.memmap.desc_size; - data->desc_version = efi.memmap.desc_version; - data->desc_size = efi.memmap.desc_size; - data->flags &= ~(EFI_MEMMAP_SLAB | EFI_MEMMAP_MEMBLOCK); - data->flags |= efi.memmap.flags & EFI_MEMMAP_LATE; - - if (slab_is_available()) { - data->flags |= EFI_MEMMAP_SLAB; - data->phys_map = __efi_memmap_alloc_late(data->size); - } else { - data->flags |= EFI_MEMMAP_MEMBLOCK; - data->phys_map = __efi_memmap_alloc_early(data->size); - } +#include +#include - if (!data->phys_map) - return -ENOMEM; - return 0; -} +#ifndef __efi_memmap_free +#define __efi_memmap_free(phys, size, flags) do { } while (0) +#endif /** * __efi_memmap_init - Common code for mapping the EFI memory map @@ -101,7 +34,7 @@ int __init efi_memmap_alloc(unsigned int num_entries, * * Returns zero on success, a negative error code on failure. */ -static int __init __efi_memmap_init(struct efi_memory_map_data *data) +int __init __efi_memmap_init(struct efi_memory_map_data *data) { struct efi_memory_map map; phys_addr_t phys_map; @@ -121,8 +54,10 @@ static int __init __efi_memmap_init(struct efi_memory_map_data *data) return -ENOMEM; } - /* NOP if data->flags & (EFI_MEMMAP_MEMBLOCK | EFI_MEMMAP_SLAB) == 0 */ - efi_memmap_free(); + if (efi.memmap.flags & (EFI_MEMMAP_MEMBLOCK | EFI_MEMMAP_SLAB)) + __efi_memmap_free(efi.memmap.phys_map, + efi.memmap.desc_size * efi.memmap.nr_map, + efi.memmap.flags); map.phys_map = data->phys_map; map.nr_map = data->size / data->desc_size; @@ -220,158 +155,3 @@ int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size) return __efi_memmap_init(&data); } - -/** - * efi_memmap_install - Install a new EFI memory map in efi.memmap - * @ctx: map allocation parameters (address, size, flags) - * - * Unlike efi_memmap_init_*(), this function does not allow the caller - * to switch from early to late mappings. It simply uses the existing - * mapping function and installs the new memmap. - * - * Returns zero on success, a negative error code on failure. - */ -int __init efi_memmap_install(struct efi_memory_map_data *data) -{ - efi_memmap_unmap(); - - return __efi_memmap_init(data); -} - -/** - * efi_memmap_split_count - Count number of additional EFI memmap entries - * @md: EFI memory descriptor to split - * @range: Address range (start, end) to split around - * - * Returns the number of additional EFI memmap entries required to - * accommodate @range. - */ -int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range) -{ - u64 m_start, m_end; - u64 start, end; - int count = 0; - - start = md->phys_addr; - end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - /* modifying range */ - m_start = range->start; - m_end = range->end; - - if (m_start <= start) { - /* split into 2 parts */ - if (start < m_end && m_end < end) - count++; - } - - if (start < m_start && m_start < end) { - /* split into 3 parts */ - if (m_end < end) - count += 2; - /* split into 2 parts */ - if (end <= m_end) - count++; - } - - return count; -} - -/** - * efi_memmap_insert - Insert a memory region in an EFI memmap - * @old_memmap: The existing EFI memory map structure - * @buf: Address of buffer to store new map - * @mem: Memory map entry to insert - * - * It is suggested that you call efi_memmap_split_count() first - * to see how large @buf needs to be. - */ -void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, - struct efi_mem_range *mem) -{ - u64 m_start, m_end, m_attr; - efi_memory_desc_t *md; - u64 start, end; - void *old, *new; - - /* modifying range */ - m_start = mem->range.start; - m_end = mem->range.end; - m_attr = mem->attribute; - - /* - * The EFI memory map deals with regions in EFI_PAGE_SIZE - * units. Ensure that the region described by 'mem' is aligned - * correctly. - */ - if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) || - !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) { - WARN_ON(1); - return; - } - - for (old = old_memmap->map, new = buf; - old < old_memmap->map_end; - old += old_memmap->desc_size, new += old_memmap->desc_size) { - - /* copy original EFI memory descriptor */ - memcpy(new, old, old_memmap->desc_size); - md = new; - start = md->phys_addr; - end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; - - if (m_start <= start && end <= m_end) - md->attribute |= m_attr; - - if (m_start <= start && - (start < m_end && m_end < end)) { - /* first part */ - md->attribute |= m_attr; - md->num_pages = (m_end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - /* latter part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_end + 1; - md->num_pages = (end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - } - - if ((start < m_start && m_start < end) && m_end < end) { - /* first part */ - md->num_pages = (m_start - md->phys_addr) >> - EFI_PAGE_SHIFT; - /* middle part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->attribute |= m_attr; - md->phys_addr = m_start; - md->num_pages = (m_end - m_start + 1) >> - EFI_PAGE_SHIFT; - /* last part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_end + 1; - md->num_pages = (end - m_end) >> - EFI_PAGE_SHIFT; - } - - if ((start < m_start && m_start < end) && - (end <= m_end)) { - /* first part */ - md->num_pages = (m_start - md->phys_addr) >> - EFI_PAGE_SHIFT; - /* latter part */ - new += old_memmap->desc_size; - memcpy(new, old, old_memmap->desc_size); - md = new; - md->phys_addr = m_start; - md->num_pages = (end - md->phys_addr + 1) >> - EFI_PAGE_SHIFT; - md->attribute |= m_attr; - } - } -} diff --git a/include/linux/efi.h b/include/linux/efi.h index 43146530374e..6a0bdef8375d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -707,18 +707,10 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, #endif extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); -extern int __init efi_memmap_alloc(unsigned int num_entries, - struct efi_memory_map_data *data); -extern void __efi_memmap_free(u64 phys, unsigned long size, - unsigned long flags); +extern int __init __efi_memmap_init(struct efi_memory_map_data *data); extern int __init efi_memmap_init_early(struct efi_memory_map_data *data); extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size); extern void __init efi_memmap_unmap(void); -extern int __init efi_memmap_install(struct efi_memory_map_data *data); -extern int __init efi_memmap_split_count(efi_memory_desc_t *md, - struct range *range); -extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, - void *buf, struct efi_mem_range *mem); #ifdef CONFIG_EFI_ESRT extern void __init efi_esrt_init(void); -- cgit v1.2.3 From d85e3e34940788578eeffd94e8b7e1d28e7278e9 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 1 Oct 2022 17:17:36 +0200 Subject: efi: xen: Set EFI_PARAVIRT for Xen dom0 boot on all architectures Currently, the EFI_PARAVIRT flag is only used by Xen dom0 boot on x86, even though other architectures also support pseudo-EFI boot, where the core kernel is invoked directly and provided with a set of data tables that resemble the ones constructed by the EFI stub, which never actually runs in that case. Let's fix this inconsistency, and always set this flag when booting dom0 via the EFI boot path. Note that Xen on x86 does not provide the EFI memory map in this case, whereas other architectures do, so move the associated EFI_PARAVIRT check into the x86 platform code. Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 8 +++++--- arch/x86/platform/efi/memmap.c | 3 +++ drivers/firmware/efi/fdtparams.c | 4 ++++ drivers/firmware/efi/memmap.c | 3 --- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ebc98a68c400..7e51c14a1ef0 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -214,9 +214,11 @@ int __init efi_memblock_x86_reserve_range(void) data.desc_size = e->efi_memdesc_size; data.desc_version = e->efi_memdesc_version; - rv = efi_memmap_init_early(&data); - if (rv) - return rv; + if (!efi_enabled(EFI_PARAVIRT)) { + rv = efi_memmap_init_early(&data); + if (rv) + return rv; + } if (add_efi_memmap || do_efi_soft_reserve()) do_add_efi_memmap(); diff --git a/arch/x86/platform/efi/memmap.c b/arch/x86/platform/efi/memmap.c index 928e8c978859..c69f8471e6d0 100644 --- a/arch/x86/platform/efi/memmap.c +++ b/arch/x86/platform/efi/memmap.c @@ -94,6 +94,9 @@ int __init efi_memmap_install(struct efi_memory_map_data *data) { efi_memmap_unmap(); + if (efi_enabled(EFI_PARAVIRT)) + return 0; + return __efi_memmap_init(data); } diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c index e901f8564ca0..0ec83ba58097 100644 --- a/drivers/firmware/efi/fdtparams.c +++ b/drivers/firmware/efi/fdtparams.c @@ -30,11 +30,13 @@ static __initconst const char name[][22] = { static __initconst const struct { const char path[17]; + u8 paravirt; const char params[PARAMCOUNT][26]; } dt_params[] = { { #ifdef CONFIG_XEN // <-------17------> .path = "/hypervisor/uefi", + .paravirt = 1, .params = { [SYSTAB] = "xen,uefi-system-table", [MMBASE] = "xen,uefi-mmap-start", @@ -121,6 +123,8 @@ u64 __init efi_get_fdt_params(struct efi_memory_map_data *mm) pr_err("Can't find property '%s' in DT!\n", pname); return 0; } + if (dt_params[i].paravirt) + set_bit(EFI_PARAVIRT, &efi.flags); return systab; } notfound: diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index e6256c48284e..a1180461a445 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -39,9 +39,6 @@ int __init __efi_memmap_init(struct efi_memory_map_data *data) struct efi_memory_map map; phys_addr_t phys_map; - if (efi_enabled(EFI_PARAVIRT)) - return 0; - phys_map = data->phys_map; if (data->flags & EFI_MEMMAP_LATE) -- cgit v1.2.3 From 36d5786a1c5533049dc1c26c75e98682bc803a31 Mon Sep 17 00:00:00 2001 From: Guilherme G. Piccoli Date: Tue, 1 Nov 2022 15:48:07 -0300 Subject: efi: pstore: Add module parameter for setting the record size By default, the efi-pstore backend hardcode the UEFI variable size as 1024 bytes. The historical reasons for that were discussed by Ard in threads [0][1]: "there is some cargo cult from prehistoric EFI times going on here, it seems. Or maybe just misinterpretation of the maximum size for the variable *name* vs the variable itself.". "OVMF has OvmfPkg/OvmfPkgX64.dsc: gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x2000 OvmfPkg/OvmfPkgX64.dsc: gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x8400 where the first one is without secure boot and the second with secure boot. Interestingly, the default is gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x400 so this is probably where this 1k number comes from." With that, and since there is not such a limit in the UEFI spec, we have the confidence to hereby add a module parameter to enable advanced users to change the UEFI record size for efi-pstore data collection, this way allowing a much easier reading of the collected log, which wouldn't be scattered anymore among many small files. Through empirical analysis we observed that extreme low values (like 8 bytes) could eventually cause writing issues, so given that and the OVMF default discussed, we limited the minimum value to 1024 bytes, which also is still the default. [0] https://lore.kernel.org/lkml/CAMj1kXF4UyRMh2Y_KakeNBHvkHhTtavASTAxXinDO1rhPe_wYg@mail.gmail.com/ [1] https://lore.kernel.org/lkml/CAMj1kXFy-2KddGu+dgebAdU9v2sindxVoiHLWuVhqYw+R=kqng@mail.gmail.com/ Signed-off-by: Guilherme G. Piccoli Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi-pstore.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 3bddc152fcd4..81ed4fc6d76d 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -10,7 +10,9 @@ MODULE_IMPORT_NS(EFIVAR); #define DUMP_NAME_LEN 66 -#define EFIVARS_DATA_SIZE_MAX 1024 +static unsigned int record_size = 1024; +module_param(record_size, uint, 0444); +MODULE_PARM_DESC(record_size, "size of each pstore UEFI var (in bytes, min/default=1024)"); static bool efivars_pstore_disable = IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); @@ -30,7 +32,7 @@ static int efi_pstore_open(struct pstore_info *psi) if (err) return err; - psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); + psi->data = kzalloc(record_size, GFP_KERNEL); if (!psi->data) return -ENOMEM; @@ -52,7 +54,7 @@ static inline u64 generic_id(u64 timestamp, unsigned int part, int count) static int efi_pstore_read_func(struct pstore_record *record, efi_char16_t *varname) { - unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX; + unsigned long wlen, size = record_size; char name[DUMP_NAME_LEN], data_type; efi_status_t status; int cnt; @@ -133,7 +135,7 @@ static ssize_t efi_pstore_read(struct pstore_record *record) efi_status_t status; for (;;) { - varname_size = EFIVARS_DATA_SIZE_MAX; + varname_size = 1024; /* * If this is the first read() call in the pstore enumeration, @@ -224,11 +226,20 @@ static __init int efivars_pstore_init(void) if (efivars_pstore_disable) return 0; - efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); + /* + * Notice that 1024 is the minimum here to prevent issues with + * decompression algorithms that were spotted during tests; + * even in the case of not using compression, smaller values would + * just pollute more the pstore FS with many small collected files. + */ + if (record_size < 1024) + record_size = 1024; + + efi_pstore_info.buf = kmalloc(record_size, GFP_KERNEL); if (!efi_pstore_info.buf) return -ENOMEM; - efi_pstore_info.bufsize = 1024; + efi_pstore_info.bufsize = record_size; if (pstore_register(&efi_pstore_info)) { kfree(efi_pstore_info.buf); -- cgit v1.2.3 From 8dfac4d8ad27c168c6e778adbebaac85b92606d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 7 Nov 2022 08:42:08 +0100 Subject: efi: runtime-maps: Clarify purpose and enable by default for kexec The current Kconfig logic for CONFIG_EFI_RUNTIME_MAPS does not convey that without it, a kexec kernel is not able to boot in EFI mode at all. So clarify this, and make the option only configurable via the menu system if CONFIG_EXPERT is set. Signed-off-by: Ard Biesheuvel Reviewed-by: Dave Young --- drivers/firmware/efi/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 0d5201e49841..552512f211a1 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -27,13 +27,13 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE using the efivars module's pstore_disable parameter. config EFI_RUNTIME_MAP - bool "Export efi runtime maps to sysfs" - depends on X86 && EFI && KEXEC_CORE - default y + bool "Export EFI runtime maps to sysfs" if EXPERT + depends on X86 && EFI + default KEXEC_CORE help - Export efi runtime memory maps to /sys/firmware/efi/runtime-map. - That memory map is used for example by kexec to set up efi virtual - mapping the 2nd kernel, but can also be used for debugging purposes. + Export EFI runtime memory regions to /sys/firmware/efi/runtime-map. + That memory map is required by the 2nd kernel to set up EFI virtual + mappings after kexec, but can also be used for debugging purposes. See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. -- cgit v1.2.3 From 1fff234de2b6047ee311c6bf88f31ad5008f2889 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 7 Nov 2022 09:17:16 +0100 Subject: efi: x86: Move EFI runtime map sysfs code to arch/x86 The EFI runtime map code is only wired up on x86, which is the only architecture that has a need for it in its implementation of kexec. So let's move this code under arch/x86 and drop all references to it from generic code. To ensure that the efi_runtime_map_init() is invoked at the appropriate time use a 'sync' subsys_initcall() that will be called right after the EFI initcall made from generic code where the original invocation of efi_runtime_map_init() resided. Signed-off-by: Ard Biesheuvel Reviewed-by: Dave Young --- arch/x86/Kconfig | 11 ++ arch/x86/include/asm/efi.h | 22 ++++ arch/x86/platform/efi/Makefile | 1 + arch/x86/platform/efi/runtime-map.c | 194 ++++++++++++++++++++++++++++++++++++ drivers/firmware/efi/Kconfig | 11 -- drivers/firmware/efi/Makefile | 1 - drivers/firmware/efi/efi.c | 5 +- drivers/firmware/efi/runtime-map.c | 193 ----------------------------------- include/linux/efi.h | 28 ------ 9 files changed, 229 insertions(+), 237 deletions(-) create mode 100644 arch/x86/platform/efi/runtime-map.c delete mode 100644 drivers/firmware/efi/runtime-map.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e2f89a7495d2..eaf84759482e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2014,6 +2014,17 @@ config EFI_MAX_FAKE_MEM Ranges can be set up to this value using comma-separated list. The default value is 8. +config EFI_RUNTIME_MAP + bool "Export EFI runtime maps to sysfs" if EXPERT + depends on EFI + default KEXEC_CORE + help + Export EFI runtime memory regions to /sys/firmware/efi/runtime-map. + That memory map is required by the 2nd kernel to set up EFI virtual + mappings after kexec, but can also be used for debugging purposes. + + See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. + source "kernel/Kconfig.hz" config KEXEC diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index f29d1450edf4..a63154e049d7 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -432,4 +432,26 @@ extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, #define arch_ima_efi_boot_mode \ ({ extern struct boot_params boot_params; boot_params.secure_boot; }) +#ifdef CONFIG_EFI_RUNTIME_MAP +int efi_get_runtime_map_size(void); +int efi_get_runtime_map_desc_size(void); +int efi_runtime_map_copy(void *buf, size_t bufsz); +#else +static inline int efi_get_runtime_map_size(void) +{ + return 0; +} + +static inline int efi_get_runtime_map_desc_size(void) +{ + return 0; +} + +static inline int efi_runtime_map_copy(void *buf, size_t bufsz) +{ + return 0; +} + +#endif + #endif /* _ASM_X86_EFI_H */ diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index ed5502a5185d..543df9a1379d 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_EFI) += memmap.o quirks.o efi.o efi_$(BITS).o \ efi_stub_$(BITS).o obj-$(CONFIG_EFI_MIXED) += efi_thunk_$(BITS).o obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o +obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o diff --git a/arch/x86/platform/efi/runtime-map.c b/arch/x86/platform/efi/runtime-map.c new file mode 100644 index 000000000000..bbee682ef8cd --- /dev/null +++ b/arch/x86/platform/efi/runtime-map.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013 Red Hat, Inc., Dave Young + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct efi_runtime_map_entry { + efi_memory_desc_t md; + struct kobject kobj; /* kobject for each entry */ +}; + +static struct efi_runtime_map_entry **map_entries; + +struct map_attribute { + struct attribute attr; + ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf); +}; + +static inline struct map_attribute *to_map_attr(struct attribute *attr) +{ + return container_of(attr, struct map_attribute, attr); +} + +static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type); +} + +#define EFI_RUNTIME_FIELD(var) entry->md.var + +#define EFI_RUNTIME_U64_ATTR_SHOW(name) \ +static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \ +{ \ + return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \ +} + +EFI_RUNTIME_U64_ATTR_SHOW(phys_addr); +EFI_RUNTIME_U64_ATTR_SHOW(virt_addr); +EFI_RUNTIME_U64_ATTR_SHOW(num_pages); +EFI_RUNTIME_U64_ATTR_SHOW(attribute); + +static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj) +{ + return container_of(kobj, struct efi_runtime_map_entry, kobj); +} + +static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct efi_runtime_map_entry *entry = to_map_entry(kobj); + struct map_attribute *map_attr = to_map_attr(attr); + + return map_attr->show(entry, buf); +} + +static struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400); +static struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400); +static struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400); +static struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400); +static struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400); + +/* + * These are default attributes that are added for every memmap entry. + */ +static struct attribute *def_attrs[] = { + &map_type_attr.attr, + &map_phys_addr_attr.attr, + &map_virt_addr_attr.attr, + &map_num_pages_attr.attr, + &map_attribute_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(def); + +static const struct sysfs_ops map_attr_ops = { + .show = map_attr_show, +}; + +static void map_release(struct kobject *kobj) +{ + struct efi_runtime_map_entry *entry; + + entry = to_map_entry(kobj); + kfree(entry); +} + +static struct kobj_type __refdata map_ktype = { + .sysfs_ops = &map_attr_ops, + .default_groups = def_groups, + .release = map_release, +}; + +static struct kset *map_kset; + +static struct efi_runtime_map_entry * +add_sysfs_runtime_map_entry(struct kobject *kobj, int nr, + efi_memory_desc_t *md) +{ + int ret; + struct efi_runtime_map_entry *entry; + + if (!map_kset) { + map_kset = kset_create_and_add("runtime-map", NULL, kobj); + if (!map_kset) + return ERR_PTR(-ENOMEM); + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + kset_unregister(map_kset); + map_kset = NULL; + return ERR_PTR(-ENOMEM); + } + + memcpy(&entry->md, md, sizeof(efi_memory_desc_t)); + + kobject_init(&entry->kobj, &map_ktype); + entry->kobj.kset = map_kset; + ret = kobject_add(&entry->kobj, NULL, "%d", nr); + if (ret) { + kobject_put(&entry->kobj); + kset_unregister(map_kset); + map_kset = NULL; + return ERR_PTR(ret); + } + + return entry; +} + +int efi_get_runtime_map_size(void) +{ + return efi.memmap.nr_map * efi.memmap.desc_size; +} + +int efi_get_runtime_map_desc_size(void) +{ + return efi.memmap.desc_size; +} + +int efi_runtime_map_copy(void *buf, size_t bufsz) +{ + size_t sz = efi_get_runtime_map_size(); + + if (sz > bufsz) + sz = bufsz; + + memcpy(buf, efi.memmap.map, sz); + return 0; +} + +static int __init efi_runtime_map_init(void) +{ + int i, j, ret = 0; + struct efi_runtime_map_entry *entry; + efi_memory_desc_t *md; + + if (!efi_enabled(EFI_MEMMAP) || !efi_kobj) + return 0; + + map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL); + if (!map_entries) { + ret = -ENOMEM; + goto out; + } + + i = 0; + for_each_efi_memory_desc(md) { + entry = add_sysfs_runtime_map_entry(efi_kobj, i, md); + if (IS_ERR(entry)) { + ret = PTR_ERR(entry); + goto out_add_entry; + } + *(map_entries + i++) = entry; + } + + return 0; +out_add_entry: + for (j = i - 1; j >= 0; j--) { + entry = *(map_entries + j); + kobject_put(&entry->kobj); + } +out: + return ret; +} +subsys_initcall_sync(efi_runtime_map_init); diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 552512f211a1..08ed88e49ea3 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -26,17 +26,6 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE backend for pstore by default. This setting can be overridden using the efivars module's pstore_disable parameter. -config EFI_RUNTIME_MAP - bool "Export EFI runtime maps to sysfs" if EXPERT - depends on X86 && EFI - default KEXEC_CORE - help - Export EFI runtime memory regions to /sys/firmware/efi/runtime-map. - That memory map is required by the 2nd kernel to set up EFI virtual - mappings after kexec, but can also be used for debugging purposes. - - See also Documentation/ABI/testing/sysfs-firmware-efi-runtime-map. - config EFI_SOFT_RESERVE bool "Reserve EFI Specific Purpose Memory" depends on EFI && EFI_STUB && ACPI_HMAT diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 8e4f0d5b26e5..7d3b08cc8c5a 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o obj-$(CONFIG_UEFI_CPER) += cper.o -obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o subdir-$(CONFIG_EFI_STUB) += libstub obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 951a42d27cf4..dfdbbc702da0 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -396,10 +396,6 @@ static int __init efisubsys_init(void) goto err_unregister; } - error = efi_runtime_map_init(efi_kobj); - if (error) - goto err_remove_group; - /* and the standard mountpoint for efivarfs */ error = sysfs_create_mount_point(efi_kobj, "efivars"); if (error) { @@ -425,6 +421,7 @@ err_unregister: generic_ops_unregister(); err_put: kobject_put(efi_kobj); + efi_kobj = NULL; destroy_workqueue(efi_rts_wq); return error; } diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c deleted file mode 100644 index 92a3d45a795c..000000000000 --- a/drivers/firmware/efi/runtime-map.c +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * linux/drivers/efi/runtime-map.c - * Copyright (C) 2013 Red Hat, Inc., Dave Young - */ - -#include -#include -#include -#include -#include -#include - -#include - -struct efi_runtime_map_entry { - efi_memory_desc_t md; - struct kobject kobj; /* kobject for each entry */ -}; - -static struct efi_runtime_map_entry **map_entries; - -struct map_attribute { - struct attribute attr; - ssize_t (*show)(struct efi_runtime_map_entry *entry, char *buf); -}; - -static inline struct map_attribute *to_map_attr(struct attribute *attr) -{ - return container_of(attr, struct map_attribute, attr); -} - -static ssize_t type_show(struct efi_runtime_map_entry *entry, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "0x%x\n", entry->md.type); -} - -#define EFI_RUNTIME_FIELD(var) entry->md.var - -#define EFI_RUNTIME_U64_ATTR_SHOW(name) \ -static ssize_t name##_show(struct efi_runtime_map_entry *entry, char *buf) \ -{ \ - return snprintf(buf, PAGE_SIZE, "0x%llx\n", EFI_RUNTIME_FIELD(name)); \ -} - -EFI_RUNTIME_U64_ATTR_SHOW(phys_addr); -EFI_RUNTIME_U64_ATTR_SHOW(virt_addr); -EFI_RUNTIME_U64_ATTR_SHOW(num_pages); -EFI_RUNTIME_U64_ATTR_SHOW(attribute); - -static inline struct efi_runtime_map_entry *to_map_entry(struct kobject *kobj) -{ - return container_of(kobj, struct efi_runtime_map_entry, kobj); -} - -static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct efi_runtime_map_entry *entry = to_map_entry(kobj); - struct map_attribute *map_attr = to_map_attr(attr); - - return map_attr->show(entry, buf); -} - -static struct map_attribute map_type_attr = __ATTR_RO_MODE(type, 0400); -static struct map_attribute map_phys_addr_attr = __ATTR_RO_MODE(phys_addr, 0400); -static struct map_attribute map_virt_addr_attr = __ATTR_RO_MODE(virt_addr, 0400); -static struct map_attribute map_num_pages_attr = __ATTR_RO_MODE(num_pages, 0400); -static struct map_attribute map_attribute_attr = __ATTR_RO_MODE(attribute, 0400); - -/* - * These are default attributes that are added for every memmap entry. - */ -static struct attribute *def_attrs[] = { - &map_type_attr.attr, - &map_phys_addr_attr.attr, - &map_virt_addr_attr.attr, - &map_num_pages_attr.attr, - &map_attribute_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(def); - -static const struct sysfs_ops map_attr_ops = { - .show = map_attr_show, -}; - -static void map_release(struct kobject *kobj) -{ - struct efi_runtime_map_entry *entry; - - entry = to_map_entry(kobj); - kfree(entry); -} - -static struct kobj_type __refdata map_ktype = { - .sysfs_ops = &map_attr_ops, - .default_groups = def_groups, - .release = map_release, -}; - -static struct kset *map_kset; - -static struct efi_runtime_map_entry * -add_sysfs_runtime_map_entry(struct kobject *kobj, int nr, - efi_memory_desc_t *md) -{ - int ret; - struct efi_runtime_map_entry *entry; - - if (!map_kset) { - map_kset = kset_create_and_add("runtime-map", NULL, kobj); - if (!map_kset) - return ERR_PTR(-ENOMEM); - } - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - kset_unregister(map_kset); - map_kset = NULL; - return ERR_PTR(-ENOMEM); - } - - memcpy(&entry->md, md, sizeof(efi_memory_desc_t)); - - kobject_init(&entry->kobj, &map_ktype); - entry->kobj.kset = map_kset; - ret = kobject_add(&entry->kobj, NULL, "%d", nr); - if (ret) { - kobject_put(&entry->kobj); - kset_unregister(map_kset); - map_kset = NULL; - return ERR_PTR(ret); - } - - return entry; -} - -int efi_get_runtime_map_size(void) -{ - return efi.memmap.nr_map * efi.memmap.desc_size; -} - -int efi_get_runtime_map_desc_size(void) -{ - return efi.memmap.desc_size; -} - -int efi_runtime_map_copy(void *buf, size_t bufsz) -{ - size_t sz = efi_get_runtime_map_size(); - - if (sz > bufsz) - sz = bufsz; - - memcpy(buf, efi.memmap.map, sz); - return 0; -} - -int __init efi_runtime_map_init(struct kobject *efi_kobj) -{ - int i, j, ret = 0; - struct efi_runtime_map_entry *entry; - efi_memory_desc_t *md; - - if (!efi_enabled(EFI_MEMMAP)) - return 0; - - map_entries = kcalloc(efi.memmap.nr_map, sizeof(entry), GFP_KERNEL); - if (!map_entries) { - ret = -ENOMEM; - goto out; - } - - i = 0; - for_each_efi_memory_desc(md) { - entry = add_sysfs_runtime_map_entry(efi_kobj, i, md); - if (IS_ERR(entry)) { - ret = PTR_ERR(entry); - goto out_add_entry; - } - *(map_entries + i++) = entry; - } - - return 0; -out_add_entry: - for (j = i - 1; j >= 0; j--) { - entry = *(map_entries + j); - kobject_put(&entry->kobj); - } -out: - return ret; -} diff --git a/include/linux/efi.h b/include/linux/efi.h index 6a0bdef8375d..400edf77bb12 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1089,34 +1089,6 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule, static inline bool efi_capsule_pending(int *reset_type) { return false; } #endif -#ifdef CONFIG_EFI_RUNTIME_MAP -int efi_runtime_map_init(struct kobject *); -int efi_get_runtime_map_size(void); -int efi_get_runtime_map_desc_size(void); -int efi_runtime_map_copy(void *buf, size_t bufsz); -#else -static inline int efi_runtime_map_init(struct kobject *kobj) -{ - return 0; -} - -static inline int efi_get_runtime_map_size(void) -{ - return 0; -} - -static inline int efi_get_runtime_map_desc_size(void) -{ - return 0; -} - -static inline int efi_runtime_map_copy(void *buf, size_t bufsz) -{ - return 0; -} - -#endif - #ifdef CONFIG_EFI extern bool efi_runtime_disabled(void); #else -- cgit v1.2.3 From d981a88c1669aa350f06db142860564e4330ed0c Mon Sep 17 00:00:00 2001 From: Jialin Zhang Date: Tue, 8 Nov 2022 10:53:10 +0800 Subject: efi: libstub: fix efi_load_initrd_dev_path() kernel-doc comment commit f4dc7fffa987 ("efi: libstub: unify initrd loading between architectures") merge the first and the second parameters into a struct without updating the kernel-doc. Let's fix it. Signed-off-by: Jialin Zhang Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 01680b11d46d..739454d8063a 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -483,8 +483,8 @@ static const struct { /** * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path - * @load_addr: pointer to store the address where the initrd was loaded - * @load_size: pointer to store the size of the loaded initrd + * @initrd: pointer of struct to store the address where the initrd was loaded + * and the size of the loaded initrd * @max: upper limit for the initrd memory allocation * * Return: -- cgit v1.2.3 From abdbf1a25daf42983a7becb6f6532ac7917951b0 Mon Sep 17 00:00:00 2001 From: Smita Koralahalli Date: Fri, 28 Oct 2022 20:09:49 +0000 Subject: efi/cper, cxl: Decode CXL Protocol Error Section Add support for decoding CXL Protocol Error Section as defined in UEFI 2.10 Section N.2.13. Do the section decoding in a new cper_cxl.c file. This new file will be used in the future for more CXL CPERs decode support. Add this to the existing UEFI_CPER config. Signed-off-by: Smita Koralahalli Reviewed-by: Jonathan Cameron Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Makefile | 2 +- drivers/firmware/efi/cper.c | 9 +++ drivers/firmware/efi/cper_cxl.c | 152 ++++++++++++++++++++++++++++++++++++++++ drivers/firmware/efi/cper_cxl.h | 66 +++++++++++++++++ 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/efi/cper_cxl.c create mode 100644 drivers/firmware/efi/cper_cxl.h diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 7d3b08cc8c5a..b51f2a4c821e 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -19,7 +19,7 @@ endif obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.o obj-$(CONFIG_EFI_ESRT) += esrt.o obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o -obj-$(CONFIG_UEFI_CPER) += cper.o +obj-$(CONFIG_UEFI_CPER) += cper.o cper_cxl.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o subdir-$(CONFIG_EFI_STUB) += libstub obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index e4e5ea7ce910..181deb9af527 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -24,6 +24,7 @@ #include #include #include +#include "cper_cxl.h" /* * CPER record ID need to be unique even after reboot, because record @@ -595,6 +596,14 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata cper_print_fw_err(newpfx, gdata, fw_err); else goto err_section_too_small; + } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { + struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata); + + printk("%ssection_type: CXL Protocol Error\n", newpfx); + if (gdata->error_data_length >= sizeof(*prot_err)) + cper_print_prot_err(newpfx, prot_err); + else + goto err_section_too_small; } else { const void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/firmware/efi/cper_cxl.c b/drivers/firmware/efi/cper_cxl.c new file mode 100644 index 000000000000..6c94af234be9 --- /dev/null +++ b/drivers/firmware/efi/cper_cxl.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UEFI Common Platform Error Record (CPER) support for CXL Section. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Smita Koralahalli + */ + +#include +#include "cper_cxl.h" + +#define PROT_ERR_VALID_AGENT_TYPE BIT_ULL(0) +#define PROT_ERR_VALID_AGENT_ADDRESS BIT_ULL(1) +#define PROT_ERR_VALID_DEVICE_ID BIT_ULL(2) +#define PROT_ERR_VALID_SERIAL_NUMBER BIT_ULL(3) +#define PROT_ERR_VALID_CAPABILITY BIT_ULL(4) +#define PROT_ERR_VALID_DVSEC BIT_ULL(5) + +static const char * const prot_err_agent_type_strs[] = { + "Restricted CXL Device", + "Restricted CXL Host Downstream Port", + "CXL Device", + "CXL Logical Device", + "CXL Fabric Manager managed Logical Device", + "CXL Root Port", + "CXL Downstream Switch Port", + "CXL Upstream Switch Port", +}; + +/* + * The layout of the enumeration and the values matches CXL Agent Type + * field in the UEFI 2.10 Section N.2.13, + */ +enum { + RCD, /* Restricted CXL Device */ + RCH_DP, /* Restricted CXL Host Downstream Port */ + DEVICE, /* CXL Device */ + LD, /* CXL Logical Device */ + FMLD, /* CXL Fabric Manager managed Logical Device */ + RP, /* CXL Root Port */ + DSP, /* CXL Downstream Switch Port */ + USP, /* CXL Upstream Switch Port */ +}; + +void cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_err) +{ + if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_TYPE) + pr_info("%s agent_type: %d, %s\n", pfx, prot_err->agent_type, + prot_err->agent_type < ARRAY_SIZE(prot_err_agent_type_strs) + ? prot_err_agent_type_strs[prot_err->agent_type] + : "unknown"); + + if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS) { + switch (prot_err->agent_type) { + /* + * According to UEFI 2.10 Section N.2.13, the term CXL Device + * is used to refer to Restricted CXL Device, CXL Device, CXL + * Logical Device or a CXL Fabric Manager Managed Logical + * Device. + */ + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + pr_info("%s agent_address: %04x:%02x:%02x.%x\n", + pfx, prot_err->agent_addr.segment, + prot_err->agent_addr.bus, + prot_err->agent_addr.device, + prot_err->agent_addr.function); + break; + case RCH_DP: + pr_info("%s rcrb_base_address: 0x%016llx\n", pfx, + prot_err->agent_addr.rcrb_base_addr); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_DEVICE_ID) { + const __u8 *class_code; + + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + pr_info("%s slot: %d\n", pfx, + prot_err->device_id.slot >> CPER_PCIE_SLOT_SHIFT); + pr_info("%s vendor_id: 0x%04x, device_id: 0x%04x\n", + pfx, prot_err->device_id.vendor_id, + prot_err->device_id.device_id); + pr_info("%s sub_vendor_id: 0x%04x, sub_device_id: 0x%04x\n", + pfx, prot_err->device_id.subsystem_vendor_id, + prot_err->device_id.subsystem_id); + class_code = prot_err->device_id.class_code; + pr_info("%s class_code: %02x%02x\n", pfx, + class_code[1], class_code[0]); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER) { + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + pr_info("%s lower_dw: 0x%08x, upper_dw: 0x%08x\n", pfx, + prot_err->dev_serial_num.lower_dw, + prot_err->dev_serial_num.upper_dw); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_CAPABILITY) { + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, + prot_err->capability, + sizeof(prot_err->capability), 0); + break; + default: + break; + } + } + + if (prot_err->valid_bits & PROT_ERR_VALID_DVSEC) { + pr_info("%s DVSEC length: 0x%04x\n", pfx, prot_err->dvsec_len); + + pr_info("%s CXL DVSEC:\n", pfx); + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, (prot_err + 1), + prot_err->dvsec_len, 0); + } +} diff --git a/drivers/firmware/efi/cper_cxl.h b/drivers/firmware/efi/cper_cxl.h new file mode 100644 index 000000000000..86bfcf7909ec --- /dev/null +++ b/drivers/firmware/efi/cper_cxl.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UEFI Common Platform Error Record (CPER) support for CXL Section. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Smita Koralahalli + */ + +#ifndef LINUX_CPER_CXL_H +#define LINUX_CPER_CXL_H + +/* CXL Protocol Error Section */ +#define CPER_SEC_CXL_PROT_ERR \ + GUID_INIT(0x80B9EFB4, 0x52B5, 0x4DE3, 0xA7, 0x77, 0x68, 0x78, \ + 0x4B, 0x77, 0x10, 0x48) + +#pragma pack(1) + +/* Compute Express Link Protocol Error Section, UEFI v2.10 sec N.2.13 */ +struct cper_sec_prot_err { + u64 valid_bits; + u8 agent_type; + u8 reserved[7]; + + /* + * Except for RCH Downstream Port, all the remaining CXL Agent + * types are uniquely identified by the PCIe compatible SBDF number. + */ + union { + u64 rcrb_base_addr; + struct { + u8 function; + u8 device; + u8 bus; + u16 segment; + u8 reserved_1[3]; + }; + } agent_addr; + + struct { + u16 vendor_id; + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_id; + u8 class_code[2]; + u16 slot; + u8 reserved_1[4]; + } device_id; + + struct { + u32 lower_dw; + u32 upper_dw; + } dev_serial_num; + + u8 capability[60]; + u16 dvsec_len; + u16 err_len; + u8 reserved_2[4]; +}; + +#pragma pack() + +void cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_err); + +#endif //__CPER_CXL_ -- cgit v1.2.3 From 2fb6999dd06f3718dcca2cf9b11ea6c9ea9da833 Mon Sep 17 00:00:00 2001 From: Smita Koralahalli Date: Fri, 28 Oct 2022 20:09:50 +0000 Subject: efi/cper, cxl: Decode CXL Error Log Print the CXL Error Log field as found in CXL Protocol Error Section. The CXL RAS Capability structure will be reused by OS First Handling and the duplication/appropriate placement will be addressed eventually. Signed-off-by: Smita Koralahalli Reviewed-by: Jonathan Cameron Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/cper_cxl.c | 27 +++++++++++++++++++++++++++ include/linux/cxl_err.h | 22 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 include/linux/cxl_err.h diff --git a/drivers/firmware/efi/cper_cxl.c b/drivers/firmware/efi/cper_cxl.c index 6c94af234be9..53e435c4f310 100644 --- a/drivers/firmware/efi/cper_cxl.c +++ b/drivers/firmware/efi/cper_cxl.c @@ -9,6 +9,7 @@ #include #include "cper_cxl.h" +#include #define PROT_ERR_VALID_AGENT_TYPE BIT_ULL(0) #define PROT_ERR_VALID_AGENT_ADDRESS BIT_ULL(1) @@ -16,6 +17,7 @@ #define PROT_ERR_VALID_SERIAL_NUMBER BIT_ULL(3) #define PROT_ERR_VALID_CAPABILITY BIT_ULL(4) #define PROT_ERR_VALID_DVSEC BIT_ULL(5) +#define PROT_ERR_VALID_ERROR_LOG BIT_ULL(6) static const char * const prot_err_agent_type_strs[] = { "Restricted CXL Device", @@ -149,4 +151,29 @@ void cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_e print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, (prot_err + 1), prot_err->dvsec_len, 0); } + + if (prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG) { + size_t size = sizeof(*prot_err) + prot_err->dvsec_len; + struct cxl_ras_capability_regs *cxl_ras; + + pr_info("%s Error log length: 0x%04x\n", pfx, prot_err->err_len); + + pr_info("%s CXL Error Log:\n", pfx); + cxl_ras = (struct cxl_ras_capability_regs *)((long)prot_err + size); + pr_info("%s cxl_ras_uncor_status: 0x%08x", pfx, + cxl_ras->uncor_status); + pr_info("%s cxl_ras_uncor_mask: 0x%08x\n", pfx, + cxl_ras->uncor_mask); + pr_info("%s cxl_ras_uncor_severity: 0x%08x\n", pfx, + cxl_ras->uncor_severity); + pr_info("%s cxl_ras_cor_status: 0x%08x", pfx, + cxl_ras->cor_status); + pr_info("%s cxl_ras_cor_mask: 0x%08x\n", pfx, + cxl_ras->cor_mask); + pr_info("%s cap_control: 0x%08x\n", pfx, + cxl_ras->cap_control); + pr_info("%s Header Log Registers:\n", pfx); + print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, cxl_ras->header_log, + sizeof(cxl_ras->header_log), 0); + } } diff --git a/include/linux/cxl_err.h b/include/linux/cxl_err.h new file mode 100644 index 000000000000..629e1bdeda44 --- /dev/null +++ b/include/linux/cxl_err.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Author: Smita Koralahalli + */ + +#ifndef LINUX_CXL_ERR_H +#define LINUX_CXL_ERR_H + +/* CXL RAS Capability Structure, CXL v3.1 sec 8.2.4.16 */ +struct cxl_ras_capability_regs { + u32 uncor_status; + u32 uncor_mask; + u32 uncor_severity; + u32 cor_status; + u32 cor_mask; + u32 cap_control; + u32 header_log[16]; +}; + +#endif //__CXL_ERR_ -- cgit v1.2.3 From 196dff2712ca5a2e651977bb2fe6b05474111a83 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Oct 2022 10:39:10 +0200 Subject: efi: random: combine bootloader provided RNG seed with RNG protocol output Instead of blindly creating the EFI random seed configuration table if the RNG protocol is implemented and works, check whether such a EFI configuration table was provided by an earlier boot stage and if so, concatenate the existing and the new seeds, leaving it up to the core code to mix it in and credit it the way it sees fit. This can be used for, e.g., systemd-boot, to pass an additional seed to Linux in a way that can be consumed by the kernel very early. In that case, the following definitions should be used to pass the seed to the EFI stub: struct linux_efi_random_seed { u32 size; // of the 'seed' array in bytes u8 seed[]; }; The memory for the struct must be allocated as EFI_ACPI_RECLAIM_MEMORY pool memory, and the address of the struct in memory should be installed as a EFI configuration table using the following GUID: LINUX_EFI_RANDOM_SEED_TABLE_GUID 1ce1e5bc-7ceb-42f2-81e5-8aadf180f57b Note that doing so is safe even on kernels that were built without this patch applied, but the seed will simply be overwritten with a seed derived from the EFI RNG protocol, if available. The recommended seed size is 32 bytes, and seeds larger than 512 bytes are considered corrupted and ignored entirely. In order to preserve forward secrecy, seeds from previous bootloaders are memzero'd out, and in order to preserve memory, those older seeds are also freed from memory. Freeing from memory without first memzeroing is not safe to do, as it's possible that nothing else will ever overwrite those pages used by EFI. Reviewed-by: Jason A. Donenfeld [ardb: incorporate Jason's followup changes to extend the maximum seed size on the consumer end, memzero() it and drop a needless printk] Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 4 ++-- drivers/firmware/efi/libstub/efistub.h | 2 ++ drivers/firmware/efi/libstub/random.c | 42 +++++++++++++++++++++++++++++----- include/linux/efi.h | 2 -- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index dfdbbc702da0..297bf3fd9acc 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -613,7 +613,7 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed)); if (seed != NULL) { - size = min(seed->size, EFI_RANDOM_SEED_SIZE); + size = min_t(u32, seed->size, SZ_1K); // sanity check early_memunmap(seed, sizeof(*seed)); } else { pr_err("Could not map UEFI random seed!\n"); @@ -622,8 +622,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, seed = early_memremap(efi_rng_seed, sizeof(*seed) + size); if (seed != NULL) { - pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, size); + memzero_explicit(seed->bits, size); early_memunmap(seed, sizeof(*seed) + size); } else { pr_err("Could not map UEFI random seed!\n"); diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 5615ad6f2958..ed405f8d09a8 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -933,6 +933,8 @@ efi_status_t efi_random_alloc(unsigned long size, unsigned long align, unsigned long *addr, unsigned long random_seed, int memory_type); +efi_status_t efi_random_get_seed(void); + efi_status_t check_platform_features(void); void *get_efi_config_table(efi_guid_t guid); diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index 33ab56769595..f85d2c066877 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -67,27 +67,43 @@ efi_status_t efi_random_get_seed(void) efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID; efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW; efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; + struct linux_efi_random_seed *prev_seed, *seed = NULL; + int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; efi_rng_protocol_t *rng = NULL; - struct linux_efi_random_seed *seed = NULL; efi_status_t status; status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); if (status != EFI_SUCCESS) return status; + /* + * Check whether a seed was provided by a prior boot stage. In that + * case, instead of overwriting it, let's create a new buffer that can + * hold both, and concatenate the existing and the new seeds. + * Note that we should read the seed size with caution, in case the + * table got corrupted in memory somehow. + */ + prev_seed = get_efi_config_table(LINUX_EFI_RANDOM_SEED_TABLE_GUID); + if (prev_seed && prev_seed->size <= 512U) { + prev_seed_size = prev_seed->size; + seed_size += prev_seed_size; + } + /* * Use EFI_ACPI_RECLAIM_MEMORY here so that it is guaranteed that the * allocation will survive a kexec reboot (although we refresh the seed * beforehand) */ status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, - sizeof(*seed) + EFI_RANDOM_SEED_SIZE, + struct_size(seed, bits, seed_size), (void **)&seed); - if (status != EFI_SUCCESS) - return status; + if (status != EFI_SUCCESS) { + efi_warn("Failed to allocate memory for RNG seed.\n"); + goto err_warn; + } status = efi_call_proto(rng, get_rng, &rng_algo_raw, - EFI_RANDOM_SEED_SIZE, seed->bits); + EFI_RANDOM_SEED_SIZE, seed->bits); if (status == EFI_UNSUPPORTED) /* @@ -100,14 +116,28 @@ efi_status_t efi_random_get_seed(void) if (status != EFI_SUCCESS) goto err_freepool; - seed->size = EFI_RANDOM_SEED_SIZE; + seed->size = seed_size; + if (prev_seed_size) + memcpy(seed->bits + EFI_RANDOM_SEED_SIZE, prev_seed->bits, + prev_seed_size); + status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); if (status != EFI_SUCCESS) goto err_freepool; + if (prev_seed_size) { + /* wipe and free the old seed if we managed to install the new one */ + memzero_explicit(prev_seed->bits, prev_seed_size); + efi_bs_call(free_pool, prev_seed); + } return EFI_SUCCESS; err_freepool: + memzero_explicit(seed, struct_size(seed, bits, seed_size)); efi_bs_call(free_pool, seed); + efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL\n"); +err_warn: + if (prev_seed) + efi_warn("Retaining bootloader-supplied seed only"); return status; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 400edf77bb12..4b27519143f5 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1133,8 +1133,6 @@ void efi_check_for_embedded_firmwares(void); static inline void efi_check_for_embedded_firmwares(void) { } #endif -efi_status_t efi_random_get_seed(void); - #define arch_efi_call_virt(p, f, args...) ((p)->f(args)) /* -- cgit v1.2.3 From 63ffb573df66aea034d07fd00483d0a3cd4fed66 Mon Sep 17 00:00:00 2001 From: Jason A. Donenfeld Date: Tue, 22 Nov 2022 03:04:00 +0100 Subject: efi: vars: prohibit reading random seed variables In anticipation of putting random seeds in EFI variables, it's important that the random GUID namespace of variables remains hidden from userspace. We accomplish this by not populating efivarfs with entries from that GUID, as well as denying the creation of new ones in that GUID. Signed-off-by: Jason A. Donenfeld Signed-off-by: Ard Biesheuvel --- fs/efivarfs/inode.c | 4 ++++ fs/efivarfs/super.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/fs/efivarfs/inode.c b/fs/efivarfs/inode.c index 939e5e242b98..617f3ad2485e 100644 --- a/fs/efivarfs/inode.c +++ b/fs/efivarfs/inode.c @@ -91,6 +91,10 @@ static int efivarfs_create(struct user_namespace *mnt_userns, struct inode *dir, err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid); if (err) goto out; + if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) { + err = -EPERM; + goto out; + } if (efivar_variable_is_removable(var->var.VendorGuid, dentry->d_name.name, namelen)) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 6780fc81cc11..07e82e246666 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -116,6 +116,9 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, int err = -ENOMEM; bool is_removable = false; + if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) + return 0; + entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return err; -- cgit v1.2.3 From a89474aaf7cf27f2299e0a00fb64c94b6ad338a9 Mon Sep 17 00:00:00 2001 From: Jason A. Donenfeld Date: Tue, 22 Nov 2022 03:04:01 +0100 Subject: efi: stub: use random seed from EFI variable EFI has a rather unique benefit that it has access to some limited non-volatile storage, where the kernel can store a random seed. Read that seed in EFISTUB and concatenate it with other seeds we wind up passing onward to the kernel in the configuration table. This is complementary to the current other two sources - previous bootloaders, and the EFI RNG protocol. Signed-off-by: Jason A. Donenfeld [ardb: check for non-NULL RNG protocol pointer, call GetVariable() without buffer first to obtain the size] Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/random.c | 68 ++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index f85d2c066877..7109b8a2dcba 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -69,13 +69,21 @@ efi_status_t efi_random_get_seed(void) efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; struct linux_efi_random_seed *prev_seed, *seed = NULL; int prev_seed_size = 0, seed_size = EFI_RANDOM_SEED_SIZE; + unsigned long nv_seed_size = 0, offset = 0; efi_rng_protocol_t *rng = NULL; efi_status_t status; status = efi_bs_call(locate_protocol, &rng_proto, NULL, (void **)&rng); if (status != EFI_SUCCESS) + seed_size = 0; + + // Call GetVariable() with a zero length buffer to obtain the size + get_efi_var(L"RandomSeed", &rng_table_guid, NULL, &nv_seed_size, NULL); + if (!seed_size && !nv_seed_size) return status; + seed_size += nv_seed_size; + /* * Check whether a seed was provided by a prior boot stage. In that * case, instead of overwriting it, let's create a new buffer that can @@ -83,7 +91,7 @@ efi_status_t efi_random_get_seed(void) * Note that we should read the seed size with caution, in case the * table got corrupted in memory somehow. */ - prev_seed = get_efi_config_table(LINUX_EFI_RANDOM_SEED_TABLE_GUID); + prev_seed = get_efi_config_table(rng_table_guid); if (prev_seed && prev_seed->size <= 512U) { prev_seed_size = prev_seed->size; seed_size += prev_seed_size; @@ -102,25 +110,53 @@ efi_status_t efi_random_get_seed(void) goto err_warn; } - status = efi_call_proto(rng, get_rng, &rng_algo_raw, - EFI_RANDOM_SEED_SIZE, seed->bits); - - if (status == EFI_UNSUPPORTED) - /* - * Use whatever algorithm we have available if the raw algorithm - * is not implemented. - */ - status = efi_call_proto(rng, get_rng, NULL, + if (rng) { + status = efi_call_proto(rng, get_rng, &rng_algo_raw, EFI_RANDOM_SEED_SIZE, seed->bits); - if (status != EFI_SUCCESS) + if (status == EFI_UNSUPPORTED) + /* + * Use whatever algorithm we have available if the raw algorithm + * is not implemented. + */ + status = efi_call_proto(rng, get_rng, NULL, + EFI_RANDOM_SEED_SIZE, seed->bits); + + if (status == EFI_SUCCESS) + offset = EFI_RANDOM_SEED_SIZE; + } + + if (nv_seed_size) { + status = get_efi_var(L"RandomSeed", &rng_table_guid, NULL, + &nv_seed_size, seed->bits + offset); + + if (status == EFI_SUCCESS) + /* + * We delete the seed here, and /hope/ that this causes + * EFI to also zero out its representation on disk. + * This is somewhat idealistic, but overwriting the + * variable with zeros is likely just as fraught too. + * TODO: in the future, maybe we can hash it forward + * instead, and write a new seed. + */ + status = set_efi_var(L"RandomSeed", &rng_table_guid, 0, + 0, NULL); + + if (status == EFI_SUCCESS) + offset += nv_seed_size; + else + memzero_explicit(seed->bits + offset, nv_seed_size); + } + + if (!offset) goto err_freepool; - seed->size = seed_size; - if (prev_seed_size) - memcpy(seed->bits + EFI_RANDOM_SEED_SIZE, prev_seed->bits, - prev_seed_size); + if (prev_seed_size) { + memcpy(seed->bits + offset, prev_seed->bits, prev_seed_size); + offset += prev_seed_size; + } + seed->size = offset; status = efi_bs_call(install_configuration_table, &rng_table_guid, seed); if (status != EFI_SUCCESS) goto err_freepool; @@ -135,7 +171,7 @@ efi_status_t efi_random_get_seed(void) err_freepool: memzero_explicit(seed, struct_size(seed, bits, seed_size)); efi_bs_call(free_pool, seed); - efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL\n"); + efi_warn("Failed to obtain seed from EFI_RNG_PROTOCOL or EFI variable\n"); err_warn: if (prev_seed) efi_warn("Retaining bootloader-supplied seed only"); -- cgit v1.2.3 From e346bebbd36b1576a3335331fed61bb48c6d8823 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 29 Nov 2022 18:23:08 +0100 Subject: efi: libstub: Always enable initrd command line loader and bump version In preparation for setting a cross-architecture baseline for EFI boot support, remove the Kconfig option that permits the command line initrd loader to be disabled. Also, bump the minor version so that any image built with the new version can be identified as supporting this. Acked-by: Leif Lindholm Reviewed-by: Daniel Kiper Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/Kconfig | 15 --------------- drivers/firmware/efi/libstub/efi-stub-helper.c | 3 +-- include/linux/pe.h | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 08ed88e49ea3..043ca31c114e 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -106,21 +106,6 @@ config EFI_ARMSTUB_DTB_LOADER functionality for bootloaders that do not have such support this option is necessary. -config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER - bool "Enable the command line initrd loader" if !X86 - depends on EFI_STUB && (EFI_GENERIC_STUB || X86) - default y if X86 - help - Select this config option to add support for the initrd= command - line parameter, allowing an initrd to be loaded into memory that - resides on a file system backed by an implementation of - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. - - This method has been superseded by the simpler LoadFile2 based - initrd loading method, but the initrd= loader is retained as it - can be used from the UEFI Shell or other generic loaders that - don't implement the Linux specific LoadFile2 method. - config EFI_BOOTLOADER_CONTROL tristate "EFI Bootloader Control" select UCS2_STRING diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 739454d8063a..f5a4bdacac64 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -538,8 +538,7 @@ efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, unsigned long soft_limit, unsigned long hard_limit) { - if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || - (IS_ENABLED(CONFIG_X86) && image == NULL)) + if (image == NULL) return EFI_UNSUPPORTED; return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, diff --git a/include/linux/pe.h b/include/linux/pe.h index 1d3836ef9d92..056a1762de90 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -29,7 +29,7 @@ * handover_offset and xloadflags fields in the bootparams structure. */ #define LINUX_EFISTUB_MAJOR_VERSION 0x1 -#define LINUX_EFISTUB_MINOR_VERSION 0x0 +#define LINUX_EFISTUB_MINOR_VERSION 0x1 #define MZ_MAGIC 0x5a4d /* "MZ" */ -- cgit v1.2.3 From 29636a5ce87bebab38c533175d72bb800a7581b8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 9 Nov 2022 15:16:11 +0100 Subject: efi: Put Linux specific magic number in the DOS header GRUB currently relies on the magic number in the image header of ARM and arm64 EFI kernel images to decide whether or not the image in question is a bootable kernel. However, the purpose of the magic number is to identify the image as one that implements the bare metal boot protocol, and so GRUB, which only does EFI boot, is limited unnecessarily to booting images that could potentially be booted in a non-EFI manner as well. This is problematic for the new zboot decompressor image format, as it can only boot in EFI mode, and must therefore not use the bare metal boot magic number in its header. For this reason, the strict magic number was dropped from GRUB, to permit essentially any kind of EFI executable to be booted via the 'linux' command, blurring the line between the linux loader and the chainloader. So let's use the same field in the DOS header that RISC-V and arm64 already use for their 'bare metal' magic numbers to store a 'generic Linux kernel' magic number, which can be used to identify bootable kernel images in PE format which don't necessarily implement a bare metal boot protocol in the same binary. Note that, in the context of EFI, the MS-DOS header is only described in terms of the fields that it shares with the hybrid PE/COFF image format, (i.e., the MS-DOS EXE magic number at offset #0 and the PE header offset at byte offset #0x3c). Since we aim for compatibility with EFI only, and not with MS-DOS or MS-Windows, we can use the remaining space in the MS-DOS header however we want. Let's set the generic magic number for x86 images as well: existing bootloaders already have their own methods to identify x86 Linux images that can be booted in a non-EFI manner, and having the magic number in place there will ease any future transitions in loader implementations to merge the x86 and non-x86 EFI boot paths. Note that 32-bit ARM already uses the same location in the header for a different purpose, but the ARM support is already widely implemented and the EFI zboot decompressor is not available on ARM anyway, so we just disregard it here. Acked-by: Leif Lindholm Reviewed-by: Daniel Kiper Signed-off-by: Ard Biesheuvel --- arch/loongarch/kernel/head.S | 3 ++- arch/x86/boot/header.S | 3 ++- drivers/firmware/efi/libstub/zboot-header.S | 3 ++- include/linux/pe.h | 7 +++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index 84970e266658..57bada6b4e93 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -25,7 +25,8 @@ _head: .dword kernel_entry /* Kernel entry point */ .dword _end - _text /* Kernel image effective size */ .quad 0 /* Kernel image load offset from start of RAM */ - .org 0x3c /* 0x20 ~ 0x3b reserved */ + .org 0x38 /* 0x20 ~ 0x37 reserved */ + .long LINUX_PE_MAGIC .long pe_header - _head /* Offset to the PE header */ pe_header: diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index f912d7770130..be8f78a7ee32 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -80,10 +80,11 @@ bs_die: ljmp $0xf000,$0xfff0 #ifdef CONFIG_EFI_STUB - .org 0x3c + .org 0x38 # # Offset to the PE header. # + .long LINUX_PE_MAGIC .long pe_header #endif /* CONFIG_EFI_STUB */ diff --git a/drivers/firmware/efi/libstub/zboot-header.S b/drivers/firmware/efi/libstub/zboot-header.S index bc2d7750d7f1..ec4525d40e0c 100644 --- a/drivers/firmware/efi/libstub/zboot-header.S +++ b/drivers/firmware/efi/libstub/zboot-header.S @@ -20,7 +20,8 @@ __efistub_efi_zboot_header: .long __efistub__gzdata_size - 12 // payload size .long 0, 0 // reserved .asciz COMP_TYPE // compression type - .org .Ldoshdr + 0x3c + .org .Ldoshdr + 0x38 + .long LINUX_PE_MAGIC .long .Lpehdr - .Ldoshdr // PE header offset .Lpehdr: diff --git a/include/linux/pe.h b/include/linux/pe.h index 056a1762de90..6ffabf1e6d03 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -31,6 +31,13 @@ #define LINUX_EFISTUB_MAJOR_VERSION 0x1 #define LINUX_EFISTUB_MINOR_VERSION 0x1 +/* + * LINUX_PE_MAGIC appears at offset 0x38 into the MS-DOS header of EFI bootable + * Linux kernel images that target the architecture as specified by the PE/COFF + * header machine type field. + */ +#define LINUX_PE_MAGIC 0x818223cd + #define MZ_MAGIC 0x5a4d /* "MZ" */ #define PE_MAGIC 0x00004550 /* "PE\0\0" */ -- cgit v1.2.3 From a37dac5c5dcfe0f1fd58513c16cdbc280a47f628 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 5 Dec 2022 09:10:17 +0100 Subject: arm64: efi: Limit allocations to 48-bit addressable physical region The UEFI spec does not mention or reason about the configured size of the virtual address space at all, but it does mention that all memory should be identity mapped using a page size of 4 KiB. This means that a LPA2 capable system that has any system memory outside of the 48-bit addressable physical range and follows the spec to the letter may serve page allocation requests from regions of memory that the kernel cannot access unless it was built with LPA2 support and enables it at runtime. So let's ensure that all page allocations are limited to the 48-bit range. Signed-off-by: Ard Biesheuvel --- arch/arm64/include/asm/efi.h | 1 + drivers/firmware/efi/libstub/alignedmem.c | 2 ++ drivers/firmware/efi/libstub/arm64-stub.c | 5 +++-- drivers/firmware/efi/libstub/efistub.h | 4 ++++ drivers/firmware/efi/libstub/mem.c | 2 ++ drivers/firmware/efi/libstub/randomalloc.c | 2 +- 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 108b115dbf5b..7c12e01c2b31 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -92,6 +92,7 @@ static inline unsigned long efi_get_kimg_min_align(void) } #define EFI_ALLOC_ALIGN SZ_64K +#define EFI_ALLOC_LIMIT ((1UL << 48) - 1) /* * On ARM systems, virtually remapped UEFI runtime services are set up in two diff --git a/drivers/firmware/efi/libstub/alignedmem.c b/drivers/firmware/efi/libstub/alignedmem.c index 174832661251..6b83c492c3b8 100644 --- a/drivers/firmware/efi/libstub/alignedmem.c +++ b/drivers/firmware/efi/libstub/alignedmem.c @@ -29,6 +29,8 @@ efi_status_t efi_allocate_pages_aligned(unsigned long size, unsigned long *addr, efi_status_t status; int slack; + max = min(max, EFI_ALLOC_LIMIT); + if (align < EFI_ALLOC_ALIGN) align = EFI_ALLOC_ALIGN; diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 7f0aab3a8ab3..7327b98d8e3f 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -113,10 +113,11 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, if (status != EFI_SUCCESS) { if (!check_image_region((u64)_text, kernel_memsize)) { efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); - } else if (IS_ALIGNED((u64)_text, min_kimg_align)) { + } else if (IS_ALIGNED((u64)_text, min_kimg_align) && + (u64)_end < EFI_ALLOC_LIMIT) { /* * Just execute from wherever we were loaded by the - * UEFI PE/COFF loader if the alignment is suitable. + * UEFI PE/COFF loader if the placement is suitable. */ *image_addr = (u64)_text; *reserve_size = 0; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index ed405f8d09a8..5b8f2c411ed8 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -29,6 +29,10 @@ #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #endif +#ifndef EFI_ALLOC_LIMIT +#define EFI_ALLOC_LIMIT ULONG_MAX +#endif + extern bool efi_nochunk; extern bool efi_nokaslr; extern int efi_loglevel; diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 03d147f17185..4f1fa302234d 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -89,6 +89,8 @@ efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, efi_physical_addr_t alloc_addr; efi_status_t status; + max = min(max, EFI_ALLOC_LIMIT); + if (EFI_ALLOC_ALIGN > EFI_PAGE_SIZE) return efi_allocate_pages_aligned(size, addr, max, EFI_ALLOC_ALIGN, diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index ec44bb7e092f..1692d19ae80f 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -29,7 +29,7 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, return 0; region_end = min(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - 1, - (u64)ULONG_MAX); + (u64)EFI_ALLOC_LIMIT); if (region_end < size) return 0; -- cgit v1.2.3 From ff7a167961d1b97e0e205f245f806e564d3505e7 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 5 Dec 2022 11:31:25 +0100 Subject: arm64: efi: Execute runtime services from a dedicated stack With the introduction of PRMT in the ACPI subsystem, the EFI rts workqueue is no longer the only caller of efi_call_virt_pointer() in the kernel. This means the EFI runtime services lock is no longer sufficient to manage concurrent calls into firmware, but also that firmware calls may occur that are not marshalled via the workqueue mechanism, but originate directly from the caller context. For added robustness, and to ensure that the runtime services have 8 KiB of stack space available as per the EFI spec, introduce a spinlock protected EFI runtime stack of 8 KiB, where the spinlock also ensures serialization between the EFI rts workqueue (which itself serializes EFI runtime calls) and other callers of efi_call_virt_pointer(). While at it, use the stack pivot to avoid reloading the shadow call stack pointer from the ordinary stack, as doing so could produce a gadget to defeat it. Signed-off-by: Ard Biesheuvel --- arch/arm64/include/asm/efi.h | 3 +++ arch/arm64/kernel/efi-rt-wrapper.S | 13 ++++++++++++- arch/arm64/kernel/efi.c | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 7c12e01c2b31..1c408ec3c8b3 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -25,6 +25,7 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); ({ \ efi_virtmap_load(); \ __efi_fpsimd_begin(); \ + spin_lock(&efi_rt_lock); \ }) #undef arch_efi_call_virt @@ -33,10 +34,12 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md); #define arch_efi_call_virt_teardown() \ ({ \ + spin_unlock(&efi_rt_lock); \ __efi_fpsimd_end(); \ efi_virtmap_unload(); \ }) +extern spinlock_t efi_rt_lock; efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...); #define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT) diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S index 75691a2641c1..b2786b968fee 100644 --- a/arch/arm64/kernel/efi-rt-wrapper.S +++ b/arch/arm64/kernel/efi-rt-wrapper.S @@ -16,6 +16,12 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) */ stp x1, x18, [sp, #16] + ldr_l x16, efi_rt_stack_top + mov sp, x16 +#ifdef CONFIG_SHADOW_CALL_STACK + str x18, [sp, #-16]! +#endif + /* * We are lucky enough that no EFI runtime services take more than * 5 arguments, so all are passed in registers rather than via the @@ -29,6 +35,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) mov x4, x6 blr x8 + mov sp, x29 ldp x1, x2, [sp, #16] cmp x2, x18 ldp x29, x30, [sp], #32 @@ -42,6 +49,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) * called with preemption disabled and a separate shadow stack is used * for interrupts. */ - mov x18, x2 +#ifdef CONFIG_SHADOW_CALL_STACK + ldr_l x18, efi_rt_stack_top + ldr x18, [x18, #-16] +#endif + b efi_handle_corrupted_x18 // tail call SYM_FUNC_END(__efi_rt_asm_wrapper) diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index a908a37f0367..386bd81ca12b 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -144,3 +144,30 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f) pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f); return s; } + +DEFINE_SPINLOCK(efi_rt_lock); + +asmlinkage u64 *efi_rt_stack_top __ro_after_init; + +/* EFI requires 8 KiB of stack space for runtime services */ +static_assert(THREAD_SIZE >= SZ_8K); + +static int __init arm64_efi_rt_init(void) +{ + void *p; + + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + return 0; + + p = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, GFP_KERNEL, + NUMA_NO_NODE, &&l); +l: if (!p) { + pr_warn("Failed to allocate EFI runtime stack\n"); + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + return -ENOMEM; + } + + efi_rt_stack_top = p + THREAD_SIZE; + return 0; +} +core_initcall(arm64_efi_rt_init); -- cgit v1.2.3 From e8dfdf3162eb549d064b8c10b1564f7e8ee82591 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Oct 2022 16:39:14 +0200 Subject: arm64: efi: Recover from synchronous exceptions occurring in firmware Unlike x86, which has machinery to deal with page faults that occur during the execution of EFI runtime services, arm64 has nothing like that, and a synchronous exception raised by firmware code brings down the whole system. With more EFI based systems appearing that were not built to run Linux (such as the Windows-on-ARM laptops based on Qualcomm SOCs), as well as the introduction of PRM (platform specific firmware routines that are callable just like EFI runtime services), we are more likely to run into issues of this sort, and it is much more likely that we can identify and work around such issues if they don't bring down the system entirely. Since we already use a EFI runtime services call wrapper in assembler, we can quite easily add some code that captures the execution state at the point where the call is made, allowing us to revert to this state and proceed execution if the call triggered a synchronous exception. Given that the kernel and the firmware don't share any data structures that could end up in an indeterminate state, we can happily continue running, as long as we mark the EFI runtime services as unavailable from that point on. Signed-off-by: Ard Biesheuvel Acked-by: Catalin Marinas --- arch/arm64/include/asm/efi.h | 8 ++++++++ arch/arm64/kernel/efi-rt-wrapper.S | 32 +++++++++++++++++++++++++++----- arch/arm64/kernel/efi.c | 22 ++++++++++++++++++++++ arch/arm64/mm/fault.c | 4 ++++ drivers/firmware/efi/runtime-wrappers.c | 1 + 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index 1c408ec3c8b3..31d13a6001df 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -14,8 +14,16 @@ #ifdef CONFIG_EFI extern void efi_init(void); + +bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg); #else #define efi_init() + +static inline +bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg) +{ + return false; +} #endif int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md); diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S index b2786b968fee..a00886410537 100644 --- a/arch/arm64/kernel/efi-rt-wrapper.S +++ b/arch/arm64/kernel/efi-rt-wrapper.S @@ -6,7 +6,7 @@ #include SYM_FUNC_START(__efi_rt_asm_wrapper) - stp x29, x30, [sp, #-32]! + stp x29, x30, [sp, #-112]! mov x29, sp /* @@ -16,11 +16,21 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) */ stp x1, x18, [sp, #16] + /* + * Preserve all callee saved registers and preserve the stack pointer + * value at the base of the EFI runtime stack so we can recover from + * synchronous exceptions occurring while executing the firmware + * routines. + */ + stp x19, x20, [sp, #32] + stp x21, x22, [sp, #48] + stp x23, x24, [sp, #64] + stp x25, x26, [sp, #80] + stp x27, x28, [sp, #96] + ldr_l x16, efi_rt_stack_top mov sp, x16 -#ifdef CONFIG_SHADOW_CALL_STACK - str x18, [sp, #-16]! -#endif + stp x18, x29, [sp, #-16]! /* * We are lucky enough that no EFI runtime services take more than @@ -38,7 +48,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) mov sp, x29 ldp x1, x2, [sp, #16] cmp x2, x18 - ldp x29, x30, [sp], #32 + ldp x29, x30, [sp], #112 b.ne 0f ret 0: @@ -56,3 +66,15 @@ SYM_FUNC_START(__efi_rt_asm_wrapper) b efi_handle_corrupted_x18 // tail call SYM_FUNC_END(__efi_rt_asm_wrapper) + +SYM_CODE_START(__efi_rt_asm_recover) + mov sp, x30 + + ldp x19, x20, [sp, #32] + ldp x21, x22, [sp, #48] + ldp x23, x24, [sp, #64] + ldp x25, x26, [sp, #80] + ldp x27, x28, [sp, #96] + ldp x29, x30, [sp], #112 + ret +SYM_CODE_END(__efi_rt_asm_recover) diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 386bd81ca12b..fab05de2e12d 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -149,6 +149,28 @@ DEFINE_SPINLOCK(efi_rt_lock); asmlinkage u64 *efi_rt_stack_top __ro_after_init; +asmlinkage efi_status_t __efi_rt_asm_recover(void); + +bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg) +{ + /* Check whether the exception occurred while running the firmware */ + if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64) + return false; + + pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg); + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + + regs->regs[0] = EFI_ABORTED; + regs->regs[30] = efi_rt_stack_top[-1]; + regs->pc = (u64)__efi_rt_asm_recover; + + if (IS_ENABLED(CONFIG_SHADOW_CALL_STACK)) + regs->regs[18] = efi_rt_stack_top[-2]; + + return true; +} + /* EFI requires 8 KiB of stack space for runtime services */ static_assert(THREAD_SIZE >= SZ_8K); diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 5b391490e045..3e9cf9826417 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -391,6 +392,9 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr, msg = "paging request"; } + if (efi_runtime_fixup_exception(regs, msg)) + return; + die_kernel_fault(msg, addr, esr, regs); } diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index f3e54f6616f0..7feee3d9c2bf 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -83,6 +83,7 @@ struct efi_runtime_work efi_rts_work; else \ pr_err("Failed to queue work to efi_rts_wq.\n"); \ \ + WARN_ON_ONCE(efi_rts_work.status == EFI_ABORTED); \ exit: \ efi_rts_work.efi_rts_id = EFI_NONE; \ efi_rts_work.status; \ -- cgit v1.2.3