From: Ard Biesheuvel <ardb@xxxxxxxxxx> Remove the dependency on the decompression wrappers used by the legacy decompressor, which do some odd things like providing a barebones malloc() implementation. Instead, implement GZIP deflate and ZSTD decompression in terms of the underlying libraries. Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx> --- drivers/firmware/efi/libstub/Makefile | 7 +- drivers/firmware/efi/libstub/efistub.h | 3 + drivers/firmware/efi/libstub/zboot-decompress-gzip.c | 66 ++++++++++++++++ drivers/firmware/efi/libstub/zboot-decompress-zstd.c | 81 ++++++++++++++++++++ drivers/firmware/efi/libstub/zboot.c | 51 ++---------- drivers/firmware/efi/libstub/zboot.lds | 1 + 6 files changed, 163 insertions(+), 46 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index ed4e8ddbe76a..e04285a7a6b9 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -89,7 +89,12 @@ lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) -zboot-obj-$(CONFIG_RISCV) := lib-clz_ctz.o lib-ashldi3.o +zboot-obj-y := zboot-decompress-gzip.o +CFLAGS_zboot-decompress-gzip.o += -I$(srctree)/lib/zlib_inflate +zboot-obj-$(CONFIG_KERNEL_ZSTD) := zboot-decompress-zstd.o lib-xxhash.o +CFLAGS_zboot-decompress-zstd.o += -I$(srctree)/lib/zstd + +zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o lib-ashldi3.o lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y) lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 76e44c185f29..172f4edab30b 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1232,4 +1232,7 @@ void process_unaccepted_memory(u64 start, u64 end); void accept_memory(phys_addr_t start, unsigned long size); void arch_accept_memory(phys_addr_t start, phys_addr_t end); +efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size); +efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen); + #endif diff --git a/drivers/firmware/efi/libstub/zboot-decompress-gzip.c b/drivers/firmware/efi/libstub/zboot-decompress-gzip.c new file mode 100644 index 000000000000..79cf8c48b033 --- /dev/null +++ b/drivers/firmware/efi/libstub/zboot-decompress-gzip.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/efi.h> +#include <linux/zlib.h> + +#include <asm/efi.h> + +#include "efistub.h" + +#include "inftrees.c" +#include "inffast.c" +#include "inflate.c" + +extern unsigned char _gzdata_start[], _gzdata_end[]; +extern u32 __aligned(1) payload_size; + +static struct z_stream_s stream; + +efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size) +{ + efi_status_t status; + int rc; + + /* skip the 10 byte header, assume no recorded filename */ + stream.next_in = _gzdata_start + 10; + stream.avail_in = _gzdata_end - stream.next_in; + + status = efi_allocate_pages(zlib_inflate_workspacesize(), + (unsigned long *)&stream.workspace, + ULONG_MAX); + if (status != EFI_SUCCESS) + return status; + + rc = zlib_inflateInit2(&stream, -MAX_WBITS); + if (rc != Z_OK) { + efi_err("failed to initialize GZIP decompressor: %d\n", rc); + status = EFI_LOAD_ERROR; + goto out; + } + + *alloc_size = payload_size; + return EFI_SUCCESS; +out: + efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace); + return status; +} + +efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen) +{ + int rc; + + stream.next_out = out; + stream.avail_out = outlen; + + rc = zlib_inflate(&stream, 0); + zlib_inflateEnd(&stream); + + efi_free(zlib_inflate_workspacesize(), (unsigned long)stream.workspace); + + if (rc != Z_STREAM_END) { + efi_err("GZIP decompression failed with status %d\n", rc); + return EFI_LOAD_ERROR; + } + + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/zboot-decompress-zstd.c b/drivers/firmware/efi/libstub/zboot-decompress-zstd.c new file mode 100644 index 000000000000..268ae53c6fda --- /dev/null +++ b/drivers/firmware/efi/libstub/zboot-decompress-zstd.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/efi.h> +#include <linux/zstd.h> + +#include <asm/efi.h> + +#include "decompress_sources.h" +#include "efistub.h" + +extern unsigned char _gzdata_start[], _gzdata_end[]; +extern u32 __aligned(1) payload_size; + +static ZSTD_inBuffer zstd_buf; +static ZSTD_DStream *dstream; +static size_t wksp_size; +static void *wksp; + +efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size) +{ + zstd_frame_header header; + efi_status_t status; + size_t ret; + + zstd_buf.src = _gzdata_start; + zstd_buf.pos = 0; + zstd_buf.size = _gzdata_end - _gzdata_start; + + ret = zstd_get_frame_header(&header, zstd_buf.src, zstd_buf.size); + if (ret != 0) { + efi_err("ZSTD-compressed data has an incomplete frame header\n"); + status = EFI_LOAD_ERROR; + goto out; + } + + if (header.windowSize > (1 << ZSTD_WINDOWLOG_MAX)) { + efi_err("ZSTD-compressed data has too large a window size\n"); + status = EFI_LOAD_ERROR; + goto out; + } + + wksp_size = zstd_dstream_workspace_bound(header.windowSize); + status = efi_allocate_pages(wksp_size, (unsigned long *)&wksp, ULONG_MAX); + if (status != EFI_SUCCESS) + goto out; + + dstream = zstd_init_dstream(header.windowSize, wksp, wksp_size); + if (!dstream) { + efi_err("Can't initialize ZSTD stream\n"); + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + *alloc_size = payload_size; + return EFI_SUCCESS; +out: + efi_free(wksp_size, (unsigned long)wksp); + return status; +} + +efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen) +{ + ZSTD_outBuffer zstd_dec; + size_t ret; + int retval; + + zstd_dec.dst = out; + zstd_dec.pos = 0; + zstd_dec.size = outlen; + + ret = zstd_decompress_stream(dstream, &zstd_dec, &zstd_buf); + efi_free(wksp_size, (unsigned long)wksp); + + retval = zstd_get_error_code(ret); + if (retval) { + efi_err("ZSTD-decompression failed with status %d\n", retval); + return EFI_LOAD_ERROR; + } + + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c index af23b3c50228..4a885fbe1ccc 100644 --- a/drivers/firmware/efi/libstub/zboot.c +++ b/drivers/firmware/efi/libstub/zboot.c @@ -7,36 +7,6 @@ #include "efistub.h" -static unsigned char zboot_heap[SZ_256K] __aligned(64); -static unsigned long free_mem_ptr, free_mem_end_ptr; - -#define STATIC static -#if defined(CONFIG_KERNEL_GZIP) -#include "../../../../lib/decompress_inflate.c" -#elif defined(CONFIG_KERNEL_LZ4) -#include "../../../../lib/decompress_unlz4.c" -#elif defined(CONFIG_KERNEL_LZMA) -#include "../../../../lib/decompress_unlzma.c" -#elif defined(CONFIG_KERNEL_LZO) -#include "../../../../lib/decompress_unlzo.c" -#elif defined(CONFIG_KERNEL_XZ) -#undef memcpy -#define memcpy memcpy -#undef memmove -#define memmove memmove -#include "../../../../lib/decompress_unxz.c" -#elif defined(CONFIG_KERNEL_ZSTD) -#include "../../../../lib/decompress_unzstd.c" -#endif - -extern char efi_zboot_header[]; -extern char _gzdata_start[], _gzdata_end[]; - -static void error(char *x) -{ - efi_err("EFI decompressor: %s\n", x); -} - static unsigned long alloc_preferred_address(unsigned long alloc_size) { #ifdef EFI_KIMG_PREFERRED_ADDRESS @@ -64,22 +34,17 @@ struct screen_info *alloc_screen_info(void) 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; efi_loaded_image_t *image; efi_status_t status; 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); - status = efi_bs_call(handle_protocol, handle, &LOADED_IMAGE_PROTOCOL_GUID, (void **)&image); if (status != EFI_SUCCESS) { - error("Failed to locate parent's loaded image protocol"); + efi_err("Failed to locate parent's loaded image protocol\n"); return status; } @@ -89,9 +54,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 - 4), - EFI_ALLOC_ALIGN); + status = efi_zboot_decompress_init(&alloc_size); + if (status != EFI_SUCCESS) + return status; // If the architecture has a preferred address for the image, // try that first. @@ -127,13 +92,9 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab) } // 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; + status = efi_zboot_decompress((void *)image_base, alloc_size); + if (status != EFI_SUCCESS) goto free_image; - } efi_cache_sync_image(image_base, alloc_size); diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index af2c82f7bd90..9ecc57ff5b45 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -17,6 +17,7 @@ SECTIONS .rodata : ALIGN(8) { __efistub__gzdata_start = .; *(.gzdata) + __efistub_payload_size = . - 4; __efistub__gzdata_end = .; *(.rodata* .init.rodata* .srodata*) -- 2.47.1.613.gc27f4b7a9f-goog