Path: blob/master/arch/x86/boot/compressed/pgtable_64.c
26481 views
// SPDX-License-Identifier: GPL-2.01#include "misc.h"2#include <asm/bootparam.h>3#include <asm/bootparam_utils.h>4#include <asm/e820/types.h>5#include <asm/processor.h>6#include "../string.h"7#include "efi.h"89#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */10#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */1112/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */13unsigned int __section(".data") __pgtable_l5_enabled;14unsigned int __section(".data") pgdir_shift = 39;15unsigned int __section(".data") ptrs_per_p4d = 1;1617/* Buffer to preserve trampoline memory */18static char trampoline_save[TRAMPOLINE_32BIT_SIZE];1920/*21* Trampoline address will be printed by extract_kernel() for debugging22* purposes.23*24* Avoid putting the pointer into .bss as it will be cleared between25* configure_5level_paging() and extract_kernel().26*/27unsigned long *trampoline_32bit __section(".data");2829int cmdline_find_option_bool(const char *option);3031static unsigned long find_trampoline_placement(void)32{33unsigned long bios_start = 0, ebda_start = 0;34struct boot_e820_entry *entry;35char *signature;36int i;3738/*39* Find a suitable spot for the trampoline.40* This code is based on reserve_bios_regions().41*/4243/*44* EFI systems may not provide legacy ROM. The memory may not be mapped45* at all.46*47* Only look for values in the legacy ROM for non-EFI system.48*/49signature = (char *)&boot_params_ptr->efi_info.efi_loader_signature;50if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) &&51strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) {52ebda_start = *(unsigned short *)0x40e << 4;53bios_start = *(unsigned short *)0x413 << 10;54}5556if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)57bios_start = BIOS_START_MAX;5859if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)60bios_start = ebda_start;6162bios_start = round_down(bios_start, PAGE_SIZE);6364/* Find the first usable memory region under bios_start. */65for (i = boot_params_ptr->e820_entries - 1; i >= 0; i--) {66unsigned long new = bios_start;6768entry = &boot_params_ptr->e820_table[i];6970/* Skip all entries above bios_start. */71if (bios_start <= entry->addr)72continue;7374/* Skip non-RAM entries. */75if (entry->type != E820_TYPE_RAM)76continue;7778/* Adjust bios_start to the end of the entry if needed. */79if (bios_start > entry->addr + entry->size)80new = entry->addr + entry->size;8182/* Keep bios_start page-aligned. */83new = round_down(new, PAGE_SIZE);8485/* Skip the entry if it's too small. */86if (new - TRAMPOLINE_32BIT_SIZE < entry->addr)87continue;8889/* Protect against underflow. */90if (new - TRAMPOLINE_32BIT_SIZE > bios_start)91break;9293bios_start = new;94break;95}9697/* Place the trampoline just below the end of low memory */98return bios_start - TRAMPOLINE_32BIT_SIZE;99}100101asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)102{103void (*toggle_la57)(void *cr3);104bool l5_required = false;105106/* Initialize boot_params. Required for cmdline_find_option_bool(). */107sanitize_boot_params(bp);108boot_params_ptr = bp;109110/*111* Check if LA57 is desired and supported.112*113* There are several parts to the check:114* - if user asked to disable 5-level paging: no5lvl in cmdline115* - if the machine supports 5-level paging:116* + CPUID leaf 7 is supported117* + the leaf has the feature bit set118*/119if (!cmdline_find_option_bool("no5lvl") &&120native_cpuid_eax(0) >= 7 && (native_cpuid_ecx(7) & BIT(16))) {121l5_required = true;122123/* Initialize variables for 5-level paging */124__pgtable_l5_enabled = 1;125pgdir_shift = 48;126ptrs_per_p4d = 512;127}128129/*130* The trampoline will not be used if the paging mode is already set to131* the desired one.132*/133if (l5_required == !!(native_read_cr4() & X86_CR4_LA57))134return;135136trampoline_32bit = (unsigned long *)find_trampoline_placement();137138/* Preserve trampoline memory */139memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE);140141/* Clear trampoline memory first */142memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE);143144/* Copy trampoline code in place */145toggle_la57 = memcpy(trampoline_32bit +146TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long),147&trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE);148149/*150* Avoid the need for a stack in the 32-bit trampoline code, by using151* LJMP rather than LRET to return back to long mode. LJMP takes an152* immediate absolute address, which needs to be adjusted based on the153* placement of the trampoline.154*/155*(u32 *)((u8 *)toggle_la57 + trampoline_ljmp_imm_offset) +=156(unsigned long)toggle_la57;157158/*159* The code below prepares page table in trampoline memory.160*161* The new page table will be used by trampoline code for switching162* from 4- to 5-level paging or vice versa.163*/164165if (l5_required) {166/*167* For 4- to 5-level paging transition, set up current CR3 as168* the first and the only entry in a new top-level page table.169*/170*trampoline_32bit = __native_read_cr3() | _PAGE_TABLE_NOENC;171} else {172unsigned long src;173174/*175* For 5- to 4-level paging transition, copy page table pointed176* by first entry in the current top-level page table as our177* new top-level page table.178*179* We cannot just point to the page table from trampoline as it180* may be above 4G.181*/182src = *(unsigned long *)__native_read_cr3() & PAGE_MASK;183memcpy(trampoline_32bit, (void *)src, PAGE_SIZE);184}185186toggle_la57(trampoline_32bit);187188/*189* Move the top level page table out of trampoline memory.190*/191memcpy(pgtable, trampoline_32bit, PAGE_SIZE);192native_write_cr3((unsigned long)pgtable);193194/* Restore trampoline memory */195memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE);196}197198199