diff options
| author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2018-08-01 09:32:25 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2018-08-02 08:22:22 -0400 |
| commit | 1b3a62643660020cdc68e6139a010c06e8fc96c7 (patch) | |
| tree | 2396dad62af3c907ef91afe6daac36a64e5609fd | |
| parent | 6b4703768268d09ac928c64474fd686adf4574f9 (diff) | |
x86/boot/compressed/64: Validate trampoline placement against E820
There were two report of boot failure cased by trampoline placed into
a reserved memory region. It can happen on machines that don't report
EBDA correctly.
Fix the problem by re-validating the found address against the E820 table.
If the address is in a reserved area, find the next usable region below the
initial address.
Fixes: 3548e131ec6a ("x86/boot/compressed/64: Find a place for 32-bit trampoline")
Reported-by: Dmitry Malkin <d.malkin@real-time-systems.com>
Reported-by: youling 257 <youling257@gmail.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Link: https://lkml.kernel.org/r/20180801133225.38121-1-kirill.shutemov@linux.intel.com
| -rw-r--r-- | arch/x86/boot/compressed/pgtable_64.c | 73 |
1 files changed, 55 insertions, 18 deletions
diff --git a/arch/x86/boot/compressed/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c index 8c5107545251..9e2157371491 100644 --- a/arch/x86/boot/compressed/pgtable_64.c +++ b/arch/x86/boot/compressed/pgtable_64.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include <asm/e820/types.h> | ||
| 1 | #include <asm/processor.h> | 2 | #include <asm/processor.h> |
| 2 | #include "pgtable.h" | 3 | #include "pgtable.h" |
| 3 | #include "../string.h" | 4 | #include "../string.h" |
| @@ -34,10 +35,62 @@ unsigned long *trampoline_32bit __section(.data); | |||
| 34 | extern struct boot_params *boot_params; | 35 | extern struct boot_params *boot_params; |
| 35 | int cmdline_find_option_bool(const char *option); | 36 | int cmdline_find_option_bool(const char *option); |
| 36 | 37 | ||
| 38 | static unsigned long find_trampoline_placement(void) | ||
| 39 | { | ||
| 40 | unsigned long bios_start, ebda_start; | ||
| 41 | unsigned long trampoline_start; | ||
| 42 | struct boot_e820_entry *entry; | ||
| 43 | int i; | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Find a suitable spot for the trampoline. | ||
| 47 | * This code is based on reserve_bios_regions(). | ||
| 48 | */ | ||
| 49 | |||
| 50 | ebda_start = *(unsigned short *)0x40e << 4; | ||
| 51 | bios_start = *(unsigned short *)0x413 << 10; | ||
| 52 | |||
| 53 | if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX) | ||
| 54 | bios_start = BIOS_START_MAX; | ||
| 55 | |||
| 56 | if (ebda_start > BIOS_START_MIN && ebda_start < bios_start) | ||
| 57 | bios_start = ebda_start; | ||
| 58 | |||
| 59 | bios_start = round_down(bios_start, PAGE_SIZE); | ||
| 60 | |||
| 61 | /* Find the first usable memory region under bios_start. */ | ||
| 62 | for (i = boot_params->e820_entries - 1; i >= 0; i--) { | ||
| 63 | entry = &boot_params->e820_table[i]; | ||
| 64 | |||
| 65 | /* Skip all entries above bios_start. */ | ||
| 66 | if (bios_start <= entry->addr) | ||
| 67 | continue; | ||
| 68 | |||
| 69 | /* Skip non-RAM entries. */ | ||
| 70 | if (entry->type != E820_TYPE_RAM) | ||
| 71 | continue; | ||
| 72 | |||
| 73 | /* Adjust bios_start to the end of the entry if needed. */ | ||
| 74 | if (bios_start > entry->addr + entry->size) | ||
| 75 | bios_start = entry->addr + entry->size; | ||
| 76 | |||
| 77 | /* Keep bios_start page-aligned. */ | ||
| 78 | bios_start = round_down(bios_start, PAGE_SIZE); | ||
| 79 | |||
| 80 | /* Skip the entry if it's too small. */ | ||
| 81 | if (bios_start - TRAMPOLINE_32BIT_SIZE < entry->addr) | ||
| 82 | continue; | ||
| 83 | |||
| 84 | break; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Place the trampoline just below the end of low memory */ | ||
| 88 | return bios_start - TRAMPOLINE_32BIT_SIZE; | ||
| 89 | } | ||
| 90 | |||
| 37 | struct paging_config paging_prepare(void *rmode) | 91 | struct paging_config paging_prepare(void *rmode) |
| 38 | { | 92 | { |
| 39 | struct paging_config paging_config = {}; | 93 | struct paging_config paging_config = {}; |
| 40 | unsigned long bios_start, ebda_start; | ||
| 41 | 94 | ||
| 42 | /* Initialize boot_params. Required for cmdline_find_option_bool(). */ | 95 | /* Initialize boot_params. Required for cmdline_find_option_bool(). */ |
| 43 | boot_params = rmode; | 96 | boot_params = rmode; |
| @@ -61,23 +114,7 @@ struct paging_config paging_prepare(void *rmode) | |||
| 61 | paging_config.l5_required = 1; | 114 | paging_config.l5_required = 1; |
| 62 | } | 115 | } |
| 63 | 116 | ||
| 64 | /* | 117 | paging_config.trampoline_start = find_trampoline_placement(); |
| 65 | * Find a suitable spot for the trampoline. | ||
| 66 | * This code is based on reserve_bios_regions(). | ||
| 67 | */ | ||
| 68 | |||
| 69 | ebda_start = *(unsigned short *)0x40e << 4; | ||
| 70 | bios_start = *(unsigned short *)0x413 << 10; | ||
| 71 | |||
| 72 | if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX) | ||
| 73 | bios_start = BIOS_START_MAX; | ||
| 74 | |||
| 75 | if (ebda_start > BIOS_START_MIN && ebda_start < bios_start) | ||
| 76 | bios_start = ebda_start; | ||
| 77 | |||
| 78 | /* Place the trampoline just below the end of low memory, aligned to 4k */ | ||
| 79 | paging_config.trampoline_start = bios_start - TRAMPOLINE_32BIT_SIZE; | ||
| 80 | paging_config.trampoline_start = round_down(paging_config.trampoline_start, PAGE_SIZE); | ||
| 81 | 118 | ||
| 82 | trampoline_32bit = (unsigned long *)paging_config.trampoline_start; | 119 | trampoline_32bit = (unsigned long *)paging_config.trampoline_start; |
| 83 | 120 | ||
