diff options
Diffstat (limited to 'src/boot/efi/linux_x86.c')
-rw-r--r-- | src/boot/efi/linux_x86.c | 21 |
1 files changed, 16 insertions, 5 deletions
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c index 757902d..58b1b3c 100644 --- a/src/boot/efi/linux_x86.c +++ b/src/boot/efi/linux_x86.c @@ -7,12 +7,13 @@ * this x86 specific linux_exec function passes the initrd by setting the * corresponding fields in the setup_header struct. * - * see https://docs.kernel.org/x86/boot.html + * see https://docs.kernel.org/arch/x86/boot.html */ #include "initrd.h" #include "linux.h" #include "macro-fundamental.h" +#include "memory-util-fundamental.h" #include "util.h" #define KERNEL_SECTOR_SIZE 512u @@ -126,7 +127,8 @@ EFI_STATUS linux_exec_efi_handover( const void *linux_buffer, size_t linux_length, const void *initrd_buffer, - size_t initrd_length) { + size_t initrd_length, + size_t kernel_size_in_memory) { assert(parent); assert(linux_buffer); @@ -153,13 +155,22 @@ EFI_STATUS linux_exec_efi_handover( FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G); /* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this - * just fine, but older kernels will fail even if they otherwise have above 4G boot support. */ + * just fine, but older kernels will fail even if they otherwise have above 4G boot support. + * A PE image's memory footprint can be larger than its file size, due to unallocated virtual + * memory sections. While normally all PE headers should be taken into account, this case only + * involves x86 Linux bzImage kernel images, for which unallocated areas are only part of the last + * header, so parsing SizeOfImage and zeroeing the buffer past the image size is enough. */ _cleanup_pages_ Pages linux_relocated = {}; - if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) { + if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX || kernel_size_in_memory > linux_length) { linux_relocated = xmalloc_pages( - AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX); + AllocateMaxAddress, + EfiLoaderCode, + EFI_SIZE_TO_PAGES(kernel_size_in_memory > linux_length ? kernel_size_in_memory : linux_length), + UINT32_MAX); linux_buffer = memcpy( PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length); + if (kernel_size_in_memory > linux_length) + memzero((uint8_t *) linux_buffer + linux_length, kernel_size_in_memory - linux_length); } _cleanup_pages_ Pages initrd_relocated = {}; |