Skip to content

Commit

Permalink
x86/boot/compressed/64: Find a place for 32-bit trampoline
Browse files Browse the repository at this point in the history
If a bootloader enables 64-bit mode with 4-level paging, we might need to
switch over to 5-level paging. The switching requires the disabling of
paging, which works fine if kernel itself is loaded below 4G.

But if the bootloader puts the kernel above 4G (not sure if anybody does
this), we would lose control as soon as paging is disabled, because the
code becomes unreachable to the CPU.

To handle the situation, we need a trampoline in lower memory that would
take care of switching on 5-level paging.

This patch finds a spot in low memory for a trampoline.

The heuristic is based on code in reserve_bios_regions().

We find the end of low memory based on BIOS and EBDA start addresses.
The trampoline is put just before end of low memory. It's mimic approach
taken to allocate memory for realtime trampoline.

Tested-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-mm@kvack.org
Link: http://lkml.kernel.org/r/20180226180451.86788-3-kirill.shutemov@linux.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Kirill A. Shutemov authored and Ingo Molnar committed Mar 12, 2018
1 parent a403d79 commit 3548e13
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 0 deletions.
6 changes: 6 additions & 0 deletions arch/x86/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "misc.h"
#include "error.h"
#include "pgtable.h"
#include "../string.h"
#include "../voffset.h"

Expand Down Expand Up @@ -372,6 +373,11 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
debug_putaddr(output_len);
debug_putaddr(kernel_total_size);

#ifdef CONFIG_X86_64
/* Report address of 32-bit trampoline */
debug_putaddr(trampoline_32bit);
#endif

/*
* The memory hole needed for the kernel is the larger of either
* the entire decompressed kernel plus relocation table, or the
Expand Down
11 changes: 11 additions & 0 deletions arch/x86/boot/compressed/pgtable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef BOOT_COMPRESSED_PAGETABLE_H
#define BOOT_COMPRESSED_PAGETABLE_H

#define TRAMPOLINE_32BIT_SIZE (2 * PAGE_SIZE)

#ifndef __ASSEMBLER__

extern unsigned long *trampoline_32bit;

#endif /* __ASSEMBLER__ */
#endif /* BOOT_COMPRESSED_PAGETABLE_H */
34 changes: 34 additions & 0 deletions arch/x86/boot/compressed/pgtable_64.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <asm/processor.h>
#include "pgtable.h"

/*
* __force_order is used by special_insns.h asm code to force instruction
Expand All @@ -9,14 +10,27 @@
*/
unsigned long __force_order;

#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */

struct paging_config {
unsigned long trampoline_start;
unsigned long l5_required;
};

/*
* Trampoline address will be printed by extract_kernel() for debugging
* purposes.
*
* Avoid putting the pointer into .bss as it will be cleared between
* paging_prepare() and extract_kernel().
*/
unsigned long *trampoline_32bit __section(.data);

struct paging_config paging_prepare(void)
{
struct paging_config paging_config = {};
unsigned long bios_start, ebda_start;

/*
* Check if LA57 is desired and supported.
Expand All @@ -35,5 +49,25 @@ struct paging_config paging_prepare(void)
paging_config.l5_required = 1;
}

/*
* Find a suitable spot for the trampoline.
* This code is based on reserve_bios_regions().
*/

ebda_start = *(unsigned short *)0x40e << 4;
bios_start = *(unsigned short *)0x413 << 10;

if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)
bios_start = BIOS_START_MAX;

if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)
bios_start = ebda_start;

/* Place the trampoline just below the end of low memory, aligned to 4k */
paging_config.trampoline_start = bios_start - TRAMPOLINE_32BIT_SIZE;
paging_config.trampoline_start = round_down(paging_config.trampoline_start, PAGE_SIZE);

trampoline_32bit = (unsigned long *)paging_config.trampoline_start;

return paging_config;
}

0 comments on commit 3548e13

Please sign in to comment.