Path: blob/master/arch/x86/boot/compressed/pgtable_64.c
50674 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/pgtable.h>6#include <asm/processor.h>7#include "../string.h"8#include "efi.h"910#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */11#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */1213/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */14unsigned int __section(".data") __pgtable_l5_enabled;15unsigned int __section(".data") pgdir_shift = 39;16unsigned int __section(".data") ptrs_per_p4d = 1;1718/* Buffer to preserve trampoline memory */19static char trampoline_save[TRAMPOLINE_32BIT_SIZE];2021/*22* Trampoline address will be printed by extract_kernel() for debugging23* purposes.24*25* Avoid putting the pointer into .bss as it will be cleared between26* configure_5level_paging() and extract_kernel().27*/28unsigned long *trampoline_32bit __section(".data");2930int cmdline_find_option_bool(const char *option);3132static unsigned long find_trampoline_placement(void)33{34unsigned long bios_start = 0, ebda_start = 0;35struct boot_e820_entry *entry;36char *signature;37int i;3839/*40* Find a suitable spot for the trampoline.41* This code is based on reserve_bios_regions().42*/4344/*45* EFI systems may not provide legacy ROM. The memory may not be mapped46* at all.47*48* Only look for values in the legacy ROM for non-EFI system.49*/50signature = (char *)&boot_params_ptr->efi_info.efi_loader_signature;51if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) &&52strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) {53ebda_start = *(unsigned short *)0x40e << 4;54bios_start = *(unsigned short *)0x413 << 10;55}5657if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)58bios_start = BIOS_START_MAX;5960if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)61bios_start = ebda_start;6263bios_start = round_down(bios_start, PAGE_SIZE);6465/* Find the first usable memory region under bios_start. */66for (i = boot_params_ptr->e820_entries - 1; i >= 0; i--) {67unsigned long new = bios_start;6869entry = &boot_params_ptr->e820_table[i];7071/* Skip all entries above bios_start. */72if (bios_start <= entry->addr)73continue;7475/* Skip non-RAM entries. */76if (entry->type != E820_TYPE_RAM)77continue;7879/* Adjust bios_start to the end of the entry if needed. */80if (bios_start > entry->addr + entry->size)81new = entry->addr + entry->size;8283/* Keep bios_start page-aligned. */84new = round_down(new, PAGE_SIZE);8586/* Skip the entry if it's too small. */87if (new - TRAMPOLINE_32BIT_SIZE < entry->addr)88continue;8990/* Protect against underflow. */91if (new - TRAMPOLINE_32BIT_SIZE > bios_start)92break;9394bios_start = new;95break;96}9798/* Place the trampoline just below the end of low memory */99return bios_start - TRAMPOLINE_32BIT_SIZE;100}101102asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)103{104void (*toggle_la57)(void *cr3);105bool l5_required = false;106107/* Initialize boot_params. Required for cmdline_find_option_bool(). */108sanitize_boot_params(bp);109boot_params_ptr = bp;110111/*112* Check if LA57 is desired and supported.113*114* There are several parts to the check:115* - if user asked to disable 5-level paging: no5lvl in cmdline116* - if the machine supports 5-level paging:117* + CPUID leaf 7 is supported118* + the leaf has the feature bit set119*/120if (!cmdline_find_option_bool("no5lvl") &&121native_cpuid_eax(0) >= 7 && (native_cpuid_ecx(7) & BIT(16))) {122l5_required = true;123124/* Initialize variables for 5-level paging */125__pgtable_l5_enabled = 1;126pgdir_shift = 48;127ptrs_per_p4d = 512;128}129130/*131* The trampoline will not be used if the paging mode is already set to132* the desired one.133*/134if (l5_required == !!(native_read_cr4() & X86_CR4_LA57))135return;136137trampoline_32bit = (unsigned long *)find_trampoline_placement();138139/* Preserve trampoline memory */140memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE);141142/* Clear trampoline memory first */143memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE);144145/* Copy trampoline code in place */146toggle_la57 = memcpy(trampoline_32bit +147TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long),148&trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE);149150/*151* Avoid the need for a stack in the 32-bit trampoline code, by using152* LJMP rather than LRET to return back to long mode. LJMP takes an153* immediate absolute address, which needs to be adjusted based on the154* placement of the trampoline.155*/156*(u32 *)((u8 *)toggle_la57 + trampoline_ljmp_imm_offset) +=157(unsigned long)toggle_la57;158159/*160* The code below prepares page table in trampoline memory.161*162* The new page table will be used by trampoline code for switching163* from 4- to 5-level paging or vice versa.164*/165166if (l5_required) {167/*168* For 4- to 5-level paging transition, set up current CR3 as169* the first and the only entry in a new top-level page table.170*/171*trampoline_32bit = native_read_cr3_pa() | _PAGE_TABLE_NOENC;172} else {173u64 *new_cr3;174pgd_t *pgdp;175176/*177* For 5- to 4-level paging transition, copy page table pointed178* by first entry in the current top-level page table as our179* new top-level page table.180*181* We cannot just point to the page table from trampoline as it182* may be above 4G.183*/184pgdp = (pgd_t *)native_read_cr3_pa();185new_cr3 = (u64 *)(native_pgd_val(pgdp[0]) & PTE_PFN_MASK);186memcpy(trampoline_32bit, new_cr3, PAGE_SIZE);187}188189toggle_la57(trampoline_32bit);190191/*192* Move the top level page table out of trampoline memory.193*/194memcpy(pgtable, trampoline_32bit, PAGE_SIZE);195native_write_cr3((unsigned long)pgtable);196197/* Restore trampoline memory */198memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE);199}200201202