Skip to content

Commit 3548e13

Browse files
kirylIngo Molnar
authored andcommitted
x86/boot/compressed/64: Find a place for 32-bit trampoline
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>
1 parent a403d79 commit 3548e13

File tree

3 files changed

+51
-0
lines changed

3 files changed

+51
-0
lines changed

arch/x86/boot/compressed/misc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "misc.h"
1616
#include "error.h"
17+
#include "pgtable.h"
1718
#include "../string.h"
1819
#include "../voffset.h"
1920

@@ -372,6 +373,11 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
372373
debug_putaddr(output_len);
373374
debug_putaddr(kernel_total_size);
374375

376+
#ifdef CONFIG_X86_64
377+
/* Report address of 32-bit trampoline */
378+
debug_putaddr(trampoline_32bit);
379+
#endif
380+
375381
/*
376382
* The memory hole needed for the kernel is the larger of either
377383
* the entire decompressed kernel plus relocation table, or the

arch/x86/boot/compressed/pgtable.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef BOOT_COMPRESSED_PAGETABLE_H
2+
#define BOOT_COMPRESSED_PAGETABLE_H
3+
4+
#define TRAMPOLINE_32BIT_SIZE (2 * PAGE_SIZE)
5+
6+
#ifndef __ASSEMBLER__
7+
8+
extern unsigned long *trampoline_32bit;
9+
10+
#endif /* __ASSEMBLER__ */
11+
#endif /* BOOT_COMPRESSED_PAGETABLE_H */

arch/x86/boot/compressed/pgtable_64.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <asm/processor.h>
2+
#include "pgtable.h"
23

34
/*
45
* __force_order is used by special_insns.h asm code to force instruction
@@ -9,14 +10,27 @@
910
*/
1011
unsigned long __force_order;
1112

13+
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
14+
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
15+
1216
struct paging_config {
1317
unsigned long trampoline_start;
1418
unsigned long l5_required;
1519
};
1620

21+
/*
22+
* Trampoline address will be printed by extract_kernel() for debugging
23+
* purposes.
24+
*
25+
* Avoid putting the pointer into .bss as it will be cleared between
26+
* paging_prepare() and extract_kernel().
27+
*/
28+
unsigned long *trampoline_32bit __section(.data);
29+
1730
struct paging_config paging_prepare(void)
1831
{
1932
struct paging_config paging_config = {};
33+
unsigned long bios_start, ebda_start;
2034

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

52+
/*
53+
* Find a suitable spot for the trampoline.
54+
* This code is based on reserve_bios_regions().
55+
*/
56+
57+
ebda_start = *(unsigned short *)0x40e << 4;
58+
bios_start = *(unsigned short *)0x413 << 10;
59+
60+
if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)
61+
bios_start = BIOS_START_MAX;
62+
63+
if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)
64+
bios_start = ebda_start;
65+
66+
/* Place the trampoline just below the end of low memory, aligned to 4k */
67+
paging_config.trampoline_start = bios_start - TRAMPOLINE_32BIT_SIZE;
68+
paging_config.trampoline_start = round_down(paging_config.trampoline_start, PAGE_SIZE);
69+
70+
trampoline_32bit = (unsigned long *)paging_config.trampoline_start;
71+
3872
return paging_config;
3973
}

0 commit comments

Comments
 (0)