Kirill A. Shutemov | 6f913de | 2019-02-19 10:52:24 +0300 | [diff] [blame] | 1 | #include <linux/efi.h> |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 2 | #include <asm/e820/types.h> |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 3 | #include <asm/processor.h> |
Kirill A. Shutemov | 6f913de | 2019-02-19 10:52:24 +0300 | [diff] [blame] | 4 | #include <asm/efi.h> |
Kirill A. Shutemov | 3548e13 | 2018-02-26 21:04:48 +0300 | [diff] [blame] | 5 | #include "pgtable.h" |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 6 | #include "../string.h" |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 7 | |
Kirill A. Shutemov | 3548e13 | 2018-02-26 21:04:48 +0300 | [diff] [blame] | 8 | #define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */ |
| 9 | #define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */ |
| 10 | |
Kirill A. Shutemov | 4440977 | 2018-02-09 17:22:26 +0300 | [diff] [blame] | 11 | struct paging_config { |
| 12 | unsigned long trampoline_start; |
| 13 | unsigned long l5_required; |
| 14 | }; |
| 15 | |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 16 | /* Buffer to preserve trampoline memory */ |
| 17 | static char trampoline_save[TRAMPOLINE_32BIT_SIZE]; |
| 18 | |
Kirill A. Shutemov | 3548e13 | 2018-02-26 21:04:48 +0300 | [diff] [blame] | 19 | /* |
| 20 | * Trampoline address will be printed by extract_kernel() for debugging |
| 21 | * purposes. |
| 22 | * |
| 23 | * Avoid putting the pointer into .bss as it will be cleared between |
| 24 | * paging_prepare() and extract_kernel(). |
| 25 | */ |
| 26 | unsigned long *trampoline_32bit __section(.data); |
| 27 | |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 28 | extern struct boot_params *boot_params; |
| 29 | int cmdline_find_option_bool(const char *option); |
| 30 | |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 31 | static unsigned long find_trampoline_placement(void) |
| 32 | { |
Kirill A. Shutemov | 6f913de | 2019-02-19 10:52:24 +0300 | [diff] [blame] | 33 | unsigned long bios_start = 0, ebda_start = 0; |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 34 | struct boot_e820_entry *entry; |
Kirill A. Shutemov | 6f913de | 2019-02-19 10:52:24 +0300 | [diff] [blame] | 35 | char *signature; |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 36 | int i; |
| 37 | |
| 38 | /* |
| 39 | * Find a suitable spot for the trampoline. |
| 40 | * This code is based on reserve_bios_regions(). |
| 41 | */ |
| 42 | |
Kirill A. Shutemov | 6f913de | 2019-02-19 10:52:24 +0300 | [diff] [blame] | 43 | /* |
| 44 | * EFI systems may not provide legacy ROM. The memory may not be mapped |
| 45 | * at all. |
| 46 | * |
| 47 | * Only look for values in the legacy ROM for non-EFI system. |
| 48 | */ |
| 49 | signature = (char *)&boot_params->efi_info.efi_loader_signature; |
| 50 | if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) && |
| 51 | strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) { |
| 52 | ebda_start = *(unsigned short *)0x40e << 4; |
| 53 | bios_start = *(unsigned short *)0x413 << 10; |
| 54 | } |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 55 | |
| 56 | if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX) |
| 57 | bios_start = BIOS_START_MAX; |
| 58 | |
| 59 | if (ebda_start > BIOS_START_MIN && ebda_start < bios_start) |
| 60 | bios_start = ebda_start; |
| 61 | |
| 62 | bios_start = round_down(bios_start, PAGE_SIZE); |
| 63 | |
| 64 | /* Find the first usable memory region under bios_start. */ |
| 65 | for (i = boot_params->e820_entries - 1; i >= 0; i--) { |
Kirill A. Shutemov | c96e848 | 2019-08-26 16:26:01 +0300 | [diff] [blame] | 66 | unsigned long new = bios_start; |
Kirill A. Shutemov | 0a46fff | 2019-08-13 16:16:54 +0300 | [diff] [blame] | 67 | |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 68 | entry = &boot_params->e820_table[i]; |
| 69 | |
| 70 | /* Skip all entries above bios_start. */ |
| 71 | if (bios_start <= entry->addr) |
| 72 | continue; |
| 73 | |
| 74 | /* Skip non-RAM entries. */ |
| 75 | if (entry->type != E820_TYPE_RAM) |
| 76 | continue; |
| 77 | |
| 78 | /* Adjust bios_start to the end of the entry if needed. */ |
| 79 | if (bios_start > entry->addr + entry->size) |
Kirill A. Shutemov | 0a46fff | 2019-08-13 16:16:54 +0300 | [diff] [blame] | 80 | new = entry->addr + entry->size; |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 81 | |
| 82 | /* Keep bios_start page-aligned. */ |
Kirill A. Shutemov | 0a46fff | 2019-08-13 16:16:54 +0300 | [diff] [blame] | 83 | new = round_down(new, PAGE_SIZE); |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 84 | |
| 85 | /* Skip the entry if it's too small. */ |
Kirill A. Shutemov | 0a46fff | 2019-08-13 16:16:54 +0300 | [diff] [blame] | 86 | if (new - TRAMPOLINE_32BIT_SIZE < entry->addr) |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 87 | continue; |
| 88 | |
Kirill A. Shutemov | 0a46fff | 2019-08-13 16:16:54 +0300 | [diff] [blame] | 89 | /* Protect against underflow. */ |
| 90 | if (new - TRAMPOLINE_32BIT_SIZE > bios_start) |
| 91 | break; |
| 92 | |
| 93 | bios_start = new; |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 94 | break; |
| 95 | } |
| 96 | |
| 97 | /* Place the trampoline just below the end of low memory */ |
| 98 | return bios_start - TRAMPOLINE_32BIT_SIZE; |
| 99 | } |
| 100 | |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 101 | struct paging_config paging_prepare(void *rmode) |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 102 | { |
Kirill A. Shutemov | 4440977 | 2018-02-09 17:22:26 +0300 | [diff] [blame] | 103 | struct paging_config paging_config = {}; |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 104 | |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 105 | /* Initialize boot_params. Required for cmdline_find_option_bool(). */ |
| 106 | boot_params = rmode; |
| 107 | |
Kirill A. Shutemov | a403d79 | 2018-02-26 21:04:47 +0300 | [diff] [blame] | 108 | /* |
| 109 | * Check if LA57 is desired and supported. |
| 110 | * |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 111 | * There are several parts to the check: |
Kirill A. Shutemov | a403d79 | 2018-02-26 21:04:47 +0300 | [diff] [blame] | 112 | * - if the kernel supports 5-level paging: CONFIG_X86_5LEVEL=y |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 113 | * - if user asked to disable 5-level paging: no5lvl in cmdline |
Kirill A. Shutemov | a403d79 | 2018-02-26 21:04:47 +0300 | [diff] [blame] | 114 | * - if the machine supports 5-level paging: |
| 115 | * + CPUID leaf 7 is supported |
| 116 | * + the leaf has the feature bit set |
| 117 | * |
| 118 | * That's substitute for boot_cpu_has() in early boot code. |
| 119 | */ |
| 120 | if (IS_ENABLED(CONFIG_X86_5LEVEL) && |
Kirill A. Shutemov | 372fddf | 2018-05-18 13:35:25 +0300 | [diff] [blame] | 121 | !cmdline_find_option_bool("no5lvl") && |
Kirill A. Shutemov | a403d79 | 2018-02-26 21:04:47 +0300 | [diff] [blame] | 122 | native_cpuid_eax(0) >= 7 && |
| 123 | (native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) { |
Kirill A. Shutemov | 4440977 | 2018-02-09 17:22:26 +0300 | [diff] [blame] | 124 | paging_config.l5_required = 1; |
Kirill A. Shutemov | a403d79 | 2018-02-26 21:04:47 +0300 | [diff] [blame] | 125 | } |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 126 | |
Kirill A. Shutemov | 1b3a626 | 2018-08-01 16:32:25 +0300 | [diff] [blame] | 127 | paging_config.trampoline_start = find_trampoline_placement(); |
Kirill A. Shutemov | 3548e13 | 2018-02-26 21:04:48 +0300 | [diff] [blame] | 128 | |
| 129 | trampoline_32bit = (unsigned long *)paging_config.trampoline_start; |
| 130 | |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 131 | /* Preserve trampoline memory */ |
| 132 | memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE); |
| 133 | |
Kirill A. Shutemov | 32fcefa | 2018-02-26 21:04:50 +0300 | [diff] [blame] | 134 | /* Clear trampoline memory first */ |
| 135 | memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE); |
| 136 | |
| 137 | /* Copy trampoline code in place */ |
| 138 | memcpy(trampoline_32bit + TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long), |
| 139 | &trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE); |
| 140 | |
Kirill A. Shutemov | e9d0e63 | 2018-02-26 21:04:51 +0300 | [diff] [blame] | 141 | /* |
| 142 | * The code below prepares page table in trampoline memory. |
| 143 | * |
| 144 | * The new page table will be used by trampoline code for switching |
| 145 | * from 4- to 5-level paging or vice versa. |
| 146 | * |
| 147 | * If switching is not required, the page table is unused: trampoline |
| 148 | * code wouldn't touch CR3. |
| 149 | */ |
| 150 | |
| 151 | /* |
| 152 | * We are not going to use the page table in trampoline memory if we |
| 153 | * are already in the desired paging mode. |
| 154 | */ |
| 155 | if (paging_config.l5_required == !!(native_read_cr4() & X86_CR4_LA57)) |
| 156 | goto out; |
| 157 | |
| 158 | if (paging_config.l5_required) { |
| 159 | /* |
| 160 | * For 4- to 5-level paging transition, set up current CR3 as |
| 161 | * the first and the only entry in a new top-level page table. |
| 162 | */ |
| 163 | trampoline_32bit[TRAMPOLINE_32BIT_PGTABLE_OFFSET] = __native_read_cr3() | _PAGE_TABLE_NOENC; |
| 164 | } else { |
| 165 | unsigned long src; |
| 166 | |
| 167 | /* |
| 168 | * For 5- to 4-level paging transition, copy page table pointed |
| 169 | * by first entry in the current top-level page table as our |
| 170 | * new top-level page table. |
| 171 | * |
| 172 | * We cannot just point to the page table from trampoline as it |
| 173 | * may be above 4G. |
| 174 | */ |
| 175 | src = *(unsigned long *)__native_read_cr3() & PAGE_MASK; |
| 176 | memcpy(trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof(unsigned long), |
| 177 | (void *)src, PAGE_SIZE); |
| 178 | } |
| 179 | |
| 180 | out: |
Kirill A. Shutemov | 4440977 | 2018-02-09 17:22:26 +0300 | [diff] [blame] | 181 | return paging_config; |
Kirill A. Shutemov | 0852907 | 2017-12-04 15:40:55 +0300 | [diff] [blame] | 182 | } |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 183 | |
Kirill A. Shutemov | 589bb62 | 2018-05-16 11:01:29 +0300 | [diff] [blame] | 184 | void cleanup_trampoline(void *pgtable) |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 185 | { |
Kirill A. Shutemov | e9d0e63 | 2018-02-26 21:04:51 +0300 | [diff] [blame] | 186 | void *trampoline_pgtable; |
| 187 | |
Kirill A. Shutemov | 30bbf72 | 2018-05-18 13:35:22 +0300 | [diff] [blame] | 188 | trampoline_pgtable = trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof(unsigned long); |
Kirill A. Shutemov | e9d0e63 | 2018-02-26 21:04:51 +0300 | [diff] [blame] | 189 | |
| 190 | /* |
| 191 | * Move the top level page table out of trampoline memory, |
| 192 | * if it's there. |
| 193 | */ |
| 194 | if ((void *)__native_read_cr3() == trampoline_pgtable) { |
Kirill A. Shutemov | 589bb62 | 2018-05-16 11:01:29 +0300 | [diff] [blame] | 195 | memcpy(pgtable, trampoline_pgtable, PAGE_SIZE); |
| 196 | native_write_cr3((unsigned long)pgtable); |
Kirill A. Shutemov | e9d0e63 | 2018-02-26 21:04:51 +0300 | [diff] [blame] | 197 | } |
| 198 | |
Kirill A. Shutemov | fb526835 | 2018-02-26 21:04:49 +0300 | [diff] [blame] | 199 | /* Restore trampoline memory */ |
| 200 | memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE); |
| 201 | } |