From 8364f6d000ede4185a82b1f2514543ed172d4b51 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 1 Mar 2023 16:55:45 +0800 Subject: [PATCH 1/7] efi/loongarch: Reintroduce efi_relocate_kernel() to relocate kernel Since Linux-6.3, LoongArch supports PIE kernel now, so let's reintroduce efi_relocate_kernel() to relocate the core kernel. Signed-off-by: Huacai Chen Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/loongarch-stub.c | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c index eee7ed43cdfb4..72c71ae201f0d 100644 --- a/drivers/firmware/efi/libstub/loongarch-stub.c +++ b/drivers/firmware/efi/libstub/loongarch-stub.c @@ -21,26 +21,16 @@ 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; - /* - * 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 = EFI_KIMG_PREFERRED_ADDRESS; - *image_size = kernel_asize; + 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); - memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS, - (void *)&kernel_offset - kernel_offset, - kernel_fsize); + *image_addr = kernel_addr; + *image_size = kernel_asize; return status; } From 038585573d0544bc717cdc5b3be4e95206d3ee3d Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:47 +0200 Subject: [PATCH 2/7] efi/pe: Import new BTI/IBT header flags from the spec The latest version of your favorite fork of the PE/COFF spec includes a new type of header flag that is intended to be used in the context of EFI firmware to indicate to the image loader that the executable regions of an image can be mapped with BTI/IBT enforcement enabled. So let's import these definitions so we can use them in subsequent patches. Signed-off-by: Ard Biesheuvel --- include/linux/pe.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/pe.h b/include/linux/pe.h index 6ffabf1e6d039..5e1e115408702 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -118,6 +118,9 @@ #define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 #define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 +#define IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT 0x0001 +#define IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT 0x0040 + /* they actually defined 0x00000000 as well, but I think we'll skip that one. */ #define IMAGE_SCN_RESERVED_0 0x00000001 #define IMAGE_SCN_RESERVED_1 0x00000002 @@ -165,6 +168,7 @@ #define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */ #define IMAGE_DEBUG_TYPE_CODEVIEW 2 +#define IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS 20 #ifndef __ASSEMBLY__ From 8358098b9787caab8bbc93fd78d046afaed43c16 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:48 +0200 Subject: [PATCH 3/7] arm64: efi: Enable BTI codegen and add PE/COFF annotation UEFI heavily relies on so-called protocols, which are essentially tables populated with pointers to executable code, and these are invoked indirectly using BR or BLR instructions. This makes the EFI execution context vulnerable to attacks on forward edge control flow, and so it would help if we could enable hardware enforcement (BTI) on CPUs that implement it. So let's no longer disable BTI codegen for the EFI stub, and set the newly introduced PE/COFF header flag when the kernel is built with BTI landing pads. Signed-off-by: Ard Biesheuvel Reviewed-by: Mark Brown --- arch/arm64/kernel/efi-header.S | 71 ++++++++++++++++----------- drivers/firmware/efi/libstub/Makefile | 3 +- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S index d731b4655df8e..11d7f7de202d0 100644 --- a/arch/arm64/kernel/efi-header.S +++ b/arch/arm64/kernel/efi-header.S @@ -81,9 +81,47 @@ .quad 0 // CertificationTable .quad 0 // BaseRelocationTable -#ifdef CONFIG_DEBUG_EFI +#if defined(CONFIG_DEBUG_EFI) || defined(CONFIG_ARM64_BTI_KERNEL) .long .Lefi_debug_table - .L_head // DebugTable .long .Lefi_debug_table_size + + /* + * The debug table is referenced via its Relative Virtual Address (RVA), + * which is only defined for those parts of the image that are covered + * by a section declaration. Since this header is not covered by any + * section, the debug table must be emitted elsewhere. So stick it in + * the .init.rodata section instead. + * + * Note that the payloads themselves are permitted to have zero RVAs, + * which means we can simply put those right after the section headers. + */ + __INITRODATA + + .align 2 +.Lefi_debug_table: +#ifdef CONFIG_DEBUG_EFI + // EFI_IMAGE_DEBUG_DIRECTORY_ENTRY + .long 0 // Characteristics + .long 0 // TimeDateStamp + .short 0 // MajorVersion + .short 0 // MinorVersion + .long IMAGE_DEBUG_TYPE_CODEVIEW // Type + .long .Lefi_debug_entry_size // SizeOfData + .long 0 // RVA + .long .Lefi_debug_entry - .L_head // FileOffset +#endif +#ifdef CONFIG_ARM64_BTI_KERNEL + .long 0 // Characteristics + .long 0 // TimeDateStamp + .short 0 // MajorVersion + .short 0 // MinorVersion + .long IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS // Type + .long 4 // SizeOfData + .long 0 // RVA + .long .Lefi_dll_characteristics_ex - .L_head // FileOffset +#endif + .set .Lefi_debug_table_size, . - .Lefi_debug_table + .previous #endif // Section table @@ -119,33 +157,6 @@ .set .Lsection_count, (. - .Lsection_table) / 40 #ifdef CONFIG_DEBUG_EFI - /* - * The debug table is referenced via its Relative Virtual Address (RVA), - * which is only defined for those parts of the image that are covered - * by a section declaration. Since this header is not covered by any - * section, the debug table must be emitted elsewhere. So stick it in - * the .init.rodata section instead. - * - * Note that the EFI debug entry itself may legally have a zero RVA, - * which means we can simply put it right after the section headers. - */ - __INITRODATA - - .align 2 -.Lefi_debug_table: - // EFI_IMAGE_DEBUG_DIRECTORY_ENTRY - .long 0 // Characteristics - .long 0 // TimeDateStamp - .short 0 // MajorVersion - .short 0 // MinorVersion - .long IMAGE_DEBUG_TYPE_CODEVIEW // Type - .long .Lefi_debug_entry_size // SizeOfData - .long 0 // RVA - .long .Lefi_debug_entry - .L_head // FileOffset - - .set .Lefi_debug_table_size, . - .Lefi_debug_table - .previous - .Lefi_debug_entry: // EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY .ascii "NB10" // Signature @@ -157,6 +168,10 @@ .set .Lefi_debug_entry_size, . - .Lefi_debug_entry #endif +#ifdef CONFIG_ARM64_BTI_KERNEL +.Lefi_dll_characteristics_ex: + .long IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT +#endif .balign SEGMENT_ALIGN .Lefi_header_end: diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 80d85a5169fb2..3abb2b357482a 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -23,8 +23,7 @@ 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) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ - -fno-unwind-tables -fno-asynchronous-unwind-tables \ - $(call cc-option,-mbranch-protection=none) + -fno-unwind-tables -fno-asynchronous-unwind-tables cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ -DEFI_HAVE_STRCMP -fno-builtin -fpic \ From bca2f3a9406b89961fbc6c92c52c6fc1fd91d35f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:50 +0200 Subject: [PATCH 4/7] efi/zboot: Add BSS padding before compression We don't really care about the size of the decompressed image - what matters is how much space needs to be allocated for the image to execute, and this includes space for BSS that is not part of the loadable image and so it is not accounted for in the decompressed size. So let's add some zero padding to the end of the image: this compresses well, and it ensures that BSS is accounted for, and as a bonus, it will be zeroed before launching the image. Since all architectures that implement support for EFI zboot carry this value in the header in the same location, we can just grab it from the binary that is being compressed. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile.zboot | 36 +++++++++++++++------ drivers/firmware/efi/libstub/zboot-header.S | 2 +- drivers/firmware/efi/libstub/zboot.c | 6 ++-- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index ccdd6a130d986..2d78770236049 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -3,6 +3,14 @@ # to be include'd by arch/$(ARCH)/boot/Makefile after setting # EFI_ZBOOT_PAYLOAD, EFI_ZBOOT_BFD_TARGET and EFI_ZBOOT_MACH_TYPE +quiet_cmd_copy_and_pad = PAD $@ + cmd_copy_and_pad = cp $< $@ && \ + truncate -s $(shell hexdump -s16 -n4 -e '"%u"' $<) $@ + +# Pad the file to the size of the uncompressed image in memory, including BSS +$(obj)/vmlinux.bin: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE + $(call if_changed,copy_and_pad) + comp-type-$(CONFIG_KERNEL_GZIP) := gzip comp-type-$(CONFIG_KERNEL_LZ4) := lz4 comp-type-$(CONFIG_KERNEL_LZMA) := lzma @@ -10,16 +18,25 @@ comp-type-$(CONFIG_KERNEL_LZO) := lzo comp-type-$(CONFIG_KERNEL_XZ) := xzkern comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22 -# 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)) && ( \ +# 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 := 12 + +zboot-method-$(CONFIG_KERNEL_GZIP) := gzip +zboot-size-len-$(CONFIG_KERNEL_GZIP) := 8 + +# Copy the SizeOfHeaders and SizeOfCode 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_$(zboot-method-y)) + cmd_compwithsize = $(cmd_$(zboot-method-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 ) >> $@ + dd status=none if=$< bs=4 count=1 skip=23 ) >> $@ -$(obj)/vmlinuz: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE +$(obj)/vmlinuz: $(obj)/vmlinux.bin FORCE $(call if_changed,compwithsize) OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \ @@ -29,6 +46,7 @@ $(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 @@ -44,4 +62,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 vmlinux.bin vmlinuz 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 445cb646eaaaf..053aba0735949 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 - 12 // payload size + .long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size .long 0, 0 // reserved .asciz COMP_TYPE // compression type .org .Ldoshdr + 0x38 diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 6105e5e2eda46..63ece48009003 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -91,12 +91,12 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) efi_info("Decompressing Linux Kernel...\n"); // SizeOfImage from the compressee's PE/COFF header - alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4), + alloc_size = round_up(get_unaligned_le32(_gzdata_end - 12), 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); + code_size = get_unaligned_le32(_gzdata_end - 4) + + get_unaligned_le32(_gzdata_end - 8); // If the architecture has a preferred address for the image, // try that first. From 538bc0f40b364f2c7b81f11aa163f723b138b40f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:51 +0200 Subject: [PATCH 5/7] efi/zboot: Set forward edge CFI compat header flag if supported Add some plumbing to the zboot EFI header generation to set the newly introduced DllCharacteristicsEx flag associated with forward edge CFI enforcement instructions (BTI on arm64, IBT on x86) x86 does not currently uses the zboot infrastructure, so let's wire it up only for arm64. Signed-off-by: Ard Biesheuvel --- arch/arm64/boot/Makefile | 1 + drivers/firmware/efi/libstub/Makefile.zboot | 9 +++- drivers/firmware/efi/libstub/zboot-header.S | 49 ++++++++++++++------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index c65aee0884103..ae645fda90bca 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -42,5 +42,6 @@ $(obj)/Image.zst: $(obj)/Image FORCE EFI_ZBOOT_PAYLOAD := Image EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 EFI_ZBOOT_MACH_TYPE := ARM64 +EFI_ZBOOT_FORWARD_CFI := $(CONFIG_ARM64_BTI_KERNEL) include $(srctree)/drivers/firmware/efi/libstub/Makefile.zboot diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 2d78770236049..0a9dcc2b13736 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 # to be include'd by arch/$(ARCH)/boot/Makefile after setting -# EFI_ZBOOT_PAYLOAD, EFI_ZBOOT_BFD_TARGET and EFI_ZBOOT_MACH_TYPE +# EFI_ZBOOT_PAYLOAD, EFI_ZBOOT_BFD_TARGET, EFI_ZBOOT_MACH_TYPE and +# EFI_ZBOOT_FORWARD_CFI quiet_cmd_copy_and_pad = PAD $@ cmd_copy_and_pad = cp $< $@ && \ @@ -44,10 +45,14 @@ OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \ $(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE $(call if_changed,objcopy) +aflags-zboot-header-$(EFI_ZBOOT_FORWARD_CFI) := \ + -DPE_DLL_CHAR_EX=IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT + 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)\"" + -DCOMP_TYPE="\"$(comp-type-y)\"" \ + $(aflags-zboot-header-y) $(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE $(call if_changed_rule,as_o_S) diff --git a/drivers/firmware/efi/libstub/zboot-header.S b/drivers/firmware/efi/libstub/zboot-header.S index 053aba0735949..fb676ded47fa4 100644 --- a/drivers/firmware/efi/libstub/zboot-header.S +++ b/drivers/firmware/efi/libstub/zboot-header.S @@ -78,9 +78,36 @@ __efistub_efi_zboot_header: .quad 0 // ExceptionTable .quad 0 // CertificationTable .quad 0 // BaseRelocationTable -#ifdef CONFIG_DEBUG_EFI +#if defined(PE_DLL_CHAR_EX) || defined(CONFIG_DEBUG_EFI) .long .Lefi_debug_table - .Ldoshdr // DebugTable .long .Lefi_debug_table_size + + .section ".rodata", "a" + .p2align 2 +.Lefi_debug_table: + // EFI_IMAGE_DEBUG_DIRECTORY_ENTRY[] +#ifdef PE_DLL_CHAR_EX + .long 0 // Characteristics + .long 0 // TimeDateStamp + .short 0 // MajorVersion + .short 0 // MinorVersion + .long IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS // Type + .long 4 // SizeOfData + .long 0 // RVA + .long .Lefi_dll_characteristics_ex - .Ldoshdr // FileOffset +#endif +#ifdef CONFIG_DEBUG_EFI + .long 0 // Characteristics + .long 0 // TimeDateStamp + .short 0 // MajorVersion + .short 0 // MinorVersion + .long IMAGE_DEBUG_TYPE_CODEVIEW // Type + .long .Lefi_debug_entry_size // SizeOfData + .long 0 // RVA + .long .Lefi_debug_entry - .Ldoshdr // FileOffset +#endif + .set .Lefi_debug_table_size, . - .Lefi_debug_table + .previous #endif .Lsection_table: @@ -110,23 +137,11 @@ __efistub_efi_zboot_header: .set .Lsection_count, (. - .Lsection_table) / 40 +#ifdef PE_DLL_CHAR_EX +.Lefi_dll_characteristics_ex: + .long PE_DLL_CHAR_EX +#endif #ifdef CONFIG_DEBUG_EFI - .section ".rodata", "a" - .align 2 -.Lefi_debug_table: - // EFI_IMAGE_DEBUG_DIRECTORY_ENTRY - .long 0 // Characteristics - .long 0 // TimeDateStamp - .short 0 // MajorVersion - .short 0 // MinorVersion - .long IMAGE_DEBUG_TYPE_CODEVIEW // Type - .long .Lefi_debug_entry_size // SizeOfData - .long 0 // RVA - .long .Lefi_debug_entry - .Ldoshdr // FileOffset - - .set .Lefi_debug_table_size, . - .Lefi_debug_table - .previous - .Lefi_debug_entry: // EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY .ascii "NB10" // Signature From 45dd403da851124412d32e3193c21de4a51d29da Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:49 +0200 Subject: [PATCH 6/7] efi/zboot: arm64: Inject kernel code size symbol into the zboot payload The EFI zboot code is not built as part of the kernel proper, like the ordinary EFI stub, but still needs access to symbols that are defined only internally in the kernel, and are left unexposed deliberately to avoid creating ABI inadvertently that we're stuck with later. So capture the kernel code size of the kernel image, and inject it as an ELF symbol into the object that contains the compressed payload, where it will be accessible to zboot code that needs it. Signed-off-by: Ard Biesheuvel Acked-by: Mark Rutland --- arch/arm64/boot/Makefile | 3 +++ arch/arm64/kernel/image-vars.h | 4 ++++ drivers/firmware/efi/libstub/Makefile.zboot | 2 +- drivers/firmware/efi/libstub/zboot.lds | 7 +++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index ae645fda90bca..1761f5972443f 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -44,4 +44,7 @@ EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64 EFI_ZBOOT_MACH_TYPE := ARM64 EFI_ZBOOT_FORWARD_CFI := $(CONFIG_ARM64_BTI_KERNEL) +EFI_ZBOOT_OBJCOPY_FLAGS = --add-symbol zboot_code_size=0x$(shell \ + $(NM) vmlinux|grep _kernel_codesize|cut -d' ' -f1) + include $(srctree)/drivers/firmware/efi/libstub/Makefile.zboot diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 8309197c0ebd4..35f3c79595137 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -108,4 +108,8 @@ KVM_NVHE_ALIAS(kvm_protected_mode_initialized); #endif /* CONFIG_KVM */ +#ifdef CONFIG_EFI_ZBOOT +_kernel_codesize = ABSOLUTE(__inittext_end - _text); +#endif + #endif /* __ARM64_KERNEL_IMAGE_VARS_H */ diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 0a9dcc2b13736..1b101d9076fd4 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -40,7 +40,7 @@ quiet_cmd_compwithsize = $(quiet_cmd_$(zboot-method-y)) $(obj)/vmlinuz: $(obj)/vmlinux.bin FORCE $(call if_changed,compwithsize) -OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \ +OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) $(EFI_ZBOOT_OBJCOPY_FLAGS) \ --rename-section .data=.gzdata,load,alloc,readonly,contents $(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE $(call if_changed,objcopy) diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index 93d33f68333b2..ac8c0ef851581 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -2,6 +2,8 @@ ENTRY(__efistub_efi_zboot_header); +PROVIDE(zboot_code_size = ABSOLUTE(0)); + SECTIONS { .head : ALIGN(4096) { @@ -17,6 +19,11 @@ SECTIONS *(.gzdata) __efistub__gzdata_end = .; *(.rodata* .init.rodata* .srodata*) + + . = ALIGN(4); + __efistub_code_size = .; + LONG(zboot_code_size); + _etext = ALIGN(4096); . = _etext; } From 026b85796ab4d52af56f8a1c60d2613983ecd845 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Apr 2023 15:49:52 +0200 Subject: [PATCH 7/7] efi/zboot: arm64: Grab code size from ELF symbol in payload Instead of relying on a dodgy dd hack to copy the image code size from the uncompressed image's PE header to the end of the compressed image, let's grab the code size from the symbol that is injected into the ELF object by the Kbuild rules that generate the compressed payload. Signed-off-by: Ard Biesheuvel Acked-by: Mark Rutland --- drivers/firmware/efi/libstub/Makefile.zboot | 14 +++----------- drivers/firmware/efi/libstub/arm64.c | 19 +++++++++++++------ drivers/firmware/efi/libstub/efistub.h | 3 +-- drivers/firmware/efi/libstub/zboot.c | 15 ++++----------- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 1b101d9076fd4..89ef820f3b344 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -24,21 +24,13 @@ comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22 # 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 := 12 +zboot-size-len-y := 4 zboot-method-$(CONFIG_KERNEL_GZIP) := gzip -zboot-size-len-$(CONFIG_KERNEL_GZIP) := 8 - -# Copy the SizeOfHeaders and SizeOfCode 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_$(zboot-method-y)) - cmd_compwithsize = $(cmd_$(zboot-method-y)) && ( \ - dd status=none if=$< bs=4 count=1 skip=37 ; \ - dd status=none if=$< bs=4 count=1 skip=23 ) >> $@ +zboot-size-len-$(CONFIG_KERNEL_GZIP) := 0 $(obj)/vmlinuz: $(obj)/vmlinux.bin FORCE - $(call if_changed,compwithsize) + $(call if_changed,$(zboot-method-y)) OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) $(EFI_ZBOOT_OBJCOPY_FLAGS) \ --rename-section .data=.gzdata,load,alloc,readonly,contents diff --git a/drivers/firmware/efi/libstub/arm64.c b/drivers/firmware/efi/libstub/arm64.c index 8aad8c49d43f1..446e35eaf3d94 100644 --- a/drivers/firmware/efi/libstub/arm64.c +++ b/drivers/firmware/efi/libstub/arm64.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -88,9 +89,10 @@ efi_status_t check_platform_features(void) #define DCTYPE "cvau" #endif +u32 __weak code_size; + void efi_cache_sync_image(unsigned long image_base, - unsigned long alloc_size, - unsigned long code_size) + unsigned long alloc_size) { u32 ctr = read_cpuid_effective_cachetype(); u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr, @@ -98,16 +100,21 @@ void efi_cache_sync_image(unsigned long image_base, /* only perform the cache maintenance if needed for I/D coherency */ if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) { + unsigned long base = image_base; + unsigned long size = code_size; + do { - asm("dc " DCTYPE ", %0" :: "r"(image_base)); - image_base += lsize; - code_size -= lsize; - } while (code_size >= lsize); + asm("dc " DCTYPE ", %0" :: "r"(base)); + base += lsize; + size -= lsize; + } while (size >= lsize); } asm("ic ialluis"); dsb(ish); isb(); + + efi_remap_image(image_base, alloc_size, code_size); } unsigned long __weak primary_entry_offset(void) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 148013bcb5f89..67d5a20802e0b 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1066,8 +1066,7 @@ 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); + unsigned long alloc_size); struct efi_smbios_record { u8 type; diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index 63ece48009003..e5d7fa1f1d8fd 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -50,8 +50,7 @@ static unsigned long alloc_preferred_address(unsigned long alloc_size) } void __weak efi_cache_sync_image(unsigned long image_base, - unsigned long alloc_size, - unsigned long code_size) + unsigned long alloc_size) { // Provided by the arch to perform the cache maintenance necessary for // executable code loaded into memory to be safe for execution. @@ -66,7 +65,7 @@ asmlinkage efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) { unsigned long compressed_size = _gzdata_end - _gzdata_start; - unsigned long image_base, alloc_size, code_size; + unsigned long image_base, alloc_size; efi_loaded_image_t *image; efi_status_t status; char *cmdline_ptr; @@ -91,13 +90,9 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) efi_info("Decompressing Linux Kernel...\n"); // SizeOfImage from the compressee's PE/COFF header - alloc_size = round_up(get_unaligned_le32(_gzdata_end - 12), + 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 - 4) + - get_unaligned_le32(_gzdata_end - 8); - // If the architecture has a preferred address for the image, // try that first. image_base = alloc_preferred_address(alloc_size); @@ -140,9 +135,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) goto free_image; } - efi_cache_sync_image(image_base, alloc_size, code_size); - - efi_remap_image(image_base, alloc_size, code_size); + efi_cache_sync_image(image_base, alloc_size); status = efi_stub_common(handle, image, image_base, cmdline_ptr);