On Thu, 18 Aug 2022 at 18:42, Heinrich Schuchardt <heinrich.schuchardt@xxxxxxxxxxxxx> wrote: > > On 8/17/22 13:03, Ard Biesheuvel wrote: > > Implement a minimal EFI app that decompresses the real kernel image and > > launches it using the firmware's LoadImage and StartImage boot services. > > This removes the need for any arch-specific hacks. > > > > Note that on systems that have UEFI secure boot policies enabled, > > LoadImage/StartImage require images to be signed, or their hashes known > > a priori, in order to be permitted to boot. > > > > There are various possible strategies to work around this requirement, > > but they all rely either on overriding internal PI/DXE protocols (which > > are not part of the EFI spec) or omitting the firmware provided > > LoadImage() and StartImage() boot services, which is also undesirable, > > given that they encapsulate platform specific policies related to secure > > boot and measured boot, but also related to memory permissions (whether > > or not and which types of heap allocations have both write and execute > > permissions.) > > > > The only generic and truly portable way around this is to simply sign > > both the inner and the outer image with the same key/cert pair, so this > > is what is implemented here. > > > > BZIP2 has been omitted from the set of supported compression algorithms, > > given that its performance is mediocre both in speed and size, and it > > uses a disproportionate amount of memory. For optimal compression, use > > LZMA. For the fastest boot speed, use LZO. > > > > Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx> > > --- > > drivers/firmware/efi/Kconfig | 31 ++++- > > drivers/firmware/efi/libstub/Makefile | 8 +- > > drivers/firmware/efi/libstub/Makefile.zboot | 69 ++++++++++ > > drivers/firmware/efi/libstub/zboot-header.S | 139 ++++++++++++++++++++ > > drivers/firmware/efi/libstub/zboot.c | 101 ++++++++++++++ > > drivers/firmware/efi/libstub/zboot.lds | 39 ++++++ > > 6 files changed, 382 insertions(+), 5 deletions(-) > > ... > > diff --git a/drivers/firmware/efi/libstub/zboot.c b/drivers/firmware/efi/libstub/zboot.c > > new file mode 100644 > > index 000000000000..9cf968e90775 > > --- /dev/null > > +++ b/drivers/firmware/efi/libstub/zboot.c ... > > +efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, > > + efi_system_table_t *systab) > > +{ > > + static efi_guid_t loaded_image = LOADED_IMAGE_PROTOCOL_GUID; > > + efi_loaded_image_t *parent, *child; > > + unsigned long image_buffer; > > + efi_handle_t child_handle; > > + efi_status_t status; > > + 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, > > + (void **)&parent); > > + if (status != EFI_SUCCESS) { > > + log(L"Failed to locate parent's loaded image protocol\n"); > > + return status; > > + } > > + > > + status = efi_allocate_pages(uncompressed_size, &image_buffer, ULONG_MAX); > > + if (status != EFI_SUCCESS) { > > + log(L"Failed to allocate memory\n"); > > + return status; > > + } > > + > > + ret = __decompress(_gzdata_start, _gzdata_end - _gzdata_start, NULL, > > + NULL, (unsigned char *)image_buffer, 0, NULL, > > + error); > > + if (ret < 0) { > > + log(L"Decompression failed\n"); > > + return EFI_LOAD_ERROR; > > + } > > + > > + status = efi_bs_call(load_image, false, handle, NULL, > > I would prefer to pass the device path of the compressed image instead > of NULL. This way information is not lost. > That way, we will have two loaded images with different handles claiming to be loaded from the same device path - I don't think that is appropriate tbh. What we could do is define a vendor GUID for the decompressed kernel, and create a device path for it. That way, you can grab the loaded_image of the parent to obtain this information. What did you have in mind as a use case? > > + (void *)image_buffer, uncompressed_size, > > + &child_handle); > > + if (status != EFI_SUCCESS) { > > + log(L"Failed to load image\n"); > > + return status; > > + } > > + > > + status = efi_bs_call(handle_protocol, child_handle, &loaded_image, > > + (void **)&child); > > + if (status != EFI_SUCCESS) { > > + log(L"Failed to locate child's loaded image protocol\n"); > > + return status; > > + } > > + > > + // 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, NULL, NULL); > > + if (status != EFI_SUCCESS) { > > + log(L"StartImage() returned with error\n"); > > Please, pass pointers for ExitDataSize and ExitData. If ExitDataSize != > 0 a string is provided in ExitData. Return that data to the caller of > the compressed image. You may additionally print the string here. > > The caller then will then take care of freeing ExitData (via FreePool()) > and optionally log the information. > Good idea, I will add that. Thanks, Ard.