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