efi/zboot: Add BSS padding before compression
authorArd Biesheuvel <ardb@kernel.org>
Tue, 18 Apr 2023 13:49:50 +0000 (15:49 +0200)
committerArd Biesheuvel <ardb@kernel.org>
Thu, 20 Apr 2023 13:44:35 +0000 (15:44 +0200)
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 <ardb@kernel.org>
drivers/firmware/efi/libstub/Makefile.zboot
drivers/firmware/efi/libstub/zboot-header.S
drivers/firmware/efi/libstub/zboot.c

index ccdd6a130d98618ec52098bcc1cfc7f4f588c400..2d78770236049b217a2859894439855dafe31c42 100644 (file)
@@ -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
index 445cb646eaaaf1c657f572e0f2d42c63962bfe27..053aba073594936bff861c4298d6ef36037c6d5c 100644 (file)
@@ -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
index 6105e5e2eda4612b3aa59e9c3e10e25f81075d7d..63ece480090032c143dc8c4c01d711ad8d814410 100644 (file)
@@ -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.