Skip to content

Commit

Permalink
kexec: x86: Include pref_address when looking for a memory hole
Browse files Browse the repository at this point in the history
When the kernel is built relocatable, the starting address can be
anything above 1Mb on x86. However, the startup_*() entries consider
that the minumum address for the kernel for the decompression has to
be at least LOAD_PHYSICAL_ADDR which is turn the value that is provided
as pref_address in boot protocol. The boot protocol itself says:

  This field, if nonzero, represents a preferred load address for the
  kernel.  A relocating bootloader should attempt to load at this
  address if possible.

Besides that the code in the kernel (arch/x86/kernel/kexec-bzimage64.c)
has these lines (in bzImage64_load() function):

    if (header->pref_address < MIN_KERNEL_LOAD_ADDR)
        kbuf.buf_min = MIN_KERNEL_LOAD_ADDR;
    else
        kbuf.buf_min = header->pref_address;

All that said, do the same in kexec tools. Without this patch
the relocatable kernel may end up in the memory hole that is
not enough for in-place decompression and Bad Things will happen
as it's proven on Intel Merrifield, that has a reserved memory
block starting at 64Mb.

Note, it doesn't mean that kernel has no issues itself in this particular
stage, but at least we may work around some corner cases for kexec.

Signed-off-by: Andy Shevchenko <[email protected]>
Signed-off-by: Simon Horman <[email protected]>
  • Loading branch information
andy-shev authored and horms committed Nov 15, 2024
1 parent ec3e4df commit 07a6217
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 2 deletions.
6 changes: 5 additions & 1 deletion kexec/arch/i386/kexec-bzImage.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,18 @@ int do_bzImage_load(struct kexec_info *info,
if (real_mode->protocol_version >=0x0205 && relocatable_kernel) {
/* Relocatable bzImage */
unsigned long kern_align = real_mode->kernel_alignment;
unsigned long kernel32_min_addr = KERN32_BASE;
unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX;

if (kernel32_min_addr < real_mode->pref_address)
kernel32_min_addr = real_mode->pref_address;

if (kernel32_max_addr > real_mode->initrd_addr_max)
kernel32_max_addr = real_mode->initrd_addr_max;

kernel32_load_addr = add_buffer(info, kernel + kern16_size,
k_size, size, kern_align,
0x100000, kernel32_max_addr,
kernel32_min_addr, kernel32_max_addr,
1);
} else {
kernel32_load_addr = KERN32_BASE;
Expand Down
10 changes: 9 additions & 1 deletion kexec/arch/x86_64/kexec-bzImage64.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ static int do_bzImage64_load(struct kexec_info *info,
char *modified_cmdline;
unsigned long cmdline_end;
unsigned long align, addr, k_size;
unsigned long kernel64_min_addr = KERN32_BASE;
unsigned long kernel64_max_addr = -1;
unsigned kern16_size_needed;

/*
Expand Down Expand Up @@ -204,8 +206,14 @@ static int do_bzImage64_load(struct kexec_info *info,
dbgprintf("kernel init_size 0x%x\n", real_mode->init_size);
size = _ALIGN(real_mode->init_size, 4096);
align = real_mode->kernel_alignment;

if (kernel64_min_addr < real_mode->pref_address)
kernel64_min_addr = real_mode->pref_address;

addr = add_buffer(info, kernel + kern16_size, k_size,
size, align, 0x100000, -1, -1);
size, align,
kernel64_min_addr, kernel64_max_addr,
-1);
if (addr == ULONG_MAX)
die("can not load bzImage64");
dbgprintf("Loaded 64bit kernel at 0x%lx\n", addr);
Expand Down

0 comments on commit 07a6217

Please sign in to comment.