summaryrefslogtreecommitdiffstats
path: root/src/boot/efi/linux_x86.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/boot/efi/linux_x86.c')
-rw-r--r--src/boot/efi/linux_x86.c21
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 = {};