2021-06-18 10:57:38 -07:00
// SPDX-License-Identifier: GPL-2.0
# include "misc.h"
2018-08-01 16:32:25 +03:00
# include <asm/e820/types.h>
2017-12-04 15:40:55 +03:00
# include <asm/processor.h>
2018-02-26 21:04:48 +03:00
# include "pgtable.h"
2018-02-26 21:04:49 +03:00
# include "../string.h"
2022-02-02 20:24:30 +01:00
# include "efi.h"
2017-12-04 15:40:55 +03:00
2018-02-26 21:04:48 +03:00
# define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
# define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
2020-10-10 15:11:10 -04:00
# ifdef CONFIG_X86_5LEVEL
/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */
2020-10-21 19:36:07 -07:00
unsigned int __section ( " .data " ) __pgtable_l5_enabled ;
unsigned int __section ( " .data " ) pgdir_shift = 39 ;
unsigned int __section ( " .data " ) ptrs_per_p4d = 1 ;
2020-10-10 15:11:10 -04:00
# endif
2018-02-09 17:22:26 +03:00
struct paging_config {
unsigned long trampoline_start ;
unsigned long l5_required ;
} ;
2017-12-04 15:40:55 +03:00
2018-02-26 21:04:49 +03:00
/* Buffer to preserve trampoline memory */
static char trampoline_save [ TRAMPOLINE_32BIT_SIZE ] ;
2018-02-26 21:04:48 +03:00
/*
* 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 ( ) .
*/
2020-10-21 19:36:07 -07:00
unsigned long * trampoline_32bit __section ( " .data " ) ;
2018-02-26 21:04:48 +03:00
2018-05-18 13:35:25 +03:00
extern struct boot_params * boot_params ;
int cmdline_find_option_bool ( const char * option ) ;
2018-08-01 16:32:25 +03:00
static unsigned long find_trampoline_placement ( void )
{
2019-02-19 10:52:24 +03:00
unsigned long bios_start = 0 , ebda_start = 0 ;
2018-08-01 16:32:25 +03:00
struct boot_e820_entry * entry ;
2019-02-19 10:52:24 +03:00
char * signature ;
2018-08-01 16:32:25 +03:00
int i ;
/*
* Find a suitable spot for the trampoline .
* This code is based on reserve_bios_regions ( ) .
*/
2019-02-19 10:52:24 +03:00
/*
* 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 ;
}
2018-08-01 16:32:25 +03:00
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 - - ) {
2019-08-26 16:26:01 +03:00
unsigned long new = bios_start ;
2019-08-13 16:16:54 +03:00
2018-08-01 16:32:25 +03:00
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 )
2019-08-13 16:16:54 +03:00
new = entry - > addr + entry - > size ;
2018-08-01 16:32:25 +03:00
/* Keep bios_start page-aligned. */
2019-08-13 16:16:54 +03:00
new = round_down ( new , PAGE_SIZE ) ;
2018-08-01 16:32:25 +03:00
/* Skip the entry if it's too small. */
2019-08-13 16:16:54 +03:00
if ( new - TRAMPOLINE_32BIT_SIZE < entry - > addr )
2018-08-01 16:32:25 +03:00
continue ;
2019-08-13 16:16:54 +03:00
/* Protect against underflow. */
if ( new - TRAMPOLINE_32BIT_SIZE > bios_start )
break ;
bios_start = new ;
2018-08-01 16:32:25 +03:00
break ;
}
/* Place the trampoline just below the end of low memory */
return bios_start - TRAMPOLINE_32BIT_SIZE ;
}
2018-05-18 13:35:25 +03:00
struct paging_config paging_prepare ( void * rmode )
2018-02-09 17:22:26 +03:00
{
struct paging_config paging_config = { } ;
2017-12-04 15:40:55 +03:00
2018-05-18 13:35:25 +03:00
/* Initialize boot_params. Required for cmdline_find_option_bool(). */
boot_params = rmode ;
2018-02-26 21:04:47 +03:00
/*
* Check if LA57 is desired and supported .
*
2018-05-18 13:35:25 +03:00
* There are several parts to the check :
2018-02-26 21:04:47 +03:00
* - if the kernel supports 5 - level paging : CONFIG_X86_5LEVEL = y
2018-05-18 13:35:25 +03:00
* - if user asked to disable 5 - level paging : no5lvl in cmdline
2018-02-26 21:04:47 +03:00
* - 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 ) & &
2018-05-18 13:35:25 +03:00
! cmdline_find_option_bool ( " no5lvl " ) & &
2018-02-26 21:04:47 +03:00
native_cpuid_eax ( 0 ) > = 7 & &
( native_cpuid_ecx ( 7 ) & ( 1 < < ( X86_FEATURE_LA57 & 31 ) ) ) ) {
2018-02-09 17:22:26 +03:00
paging_config . l5_required = 1 ;
2018-02-26 21:04:47 +03:00
}
2017-12-04 15:40:55 +03:00
2018-08-01 16:32:25 +03:00
paging_config . trampoline_start = find_trampoline_placement ( ) ;
2018-02-26 21:04:48 +03:00
trampoline_32bit = ( unsigned long * ) paging_config . trampoline_start ;
2018-02-26 21:04:49 +03:00
/* Preserve trampoline memory */
memcpy ( trampoline_save , trampoline_32bit , TRAMPOLINE_32BIT_SIZE ) ;
2018-02-26 21:04:50 +03:00
/* 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 ) ;
2018-02-26 21:04:51 +03:00
/*
* 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 4 G .
*/
src = * ( unsigned long * ) __native_read_cr3 ( ) & PAGE_MASK ;
memcpy ( trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof ( unsigned long ) ,
( void * ) src , PAGE_SIZE ) ;
}
out :
2018-02-09 17:22:26 +03:00
return paging_config ;
2017-12-04 15:40:55 +03:00
}
2018-02-26 21:04:49 +03:00
2018-05-16 11:01:29 +03:00
void cleanup_trampoline ( void * pgtable )
2018-02-26 21:04:49 +03:00
{
2018-02-26 21:04:51 +03:00
void * trampoline_pgtable ;
2018-05-18 13:35:22 +03:00
trampoline_pgtable = trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof ( unsigned long ) ;
2018-02-26 21:04:51 +03:00
/*
* Move the top level page table out of trampoline memory ,
* if it ' s there .
*/
if ( ( void * ) __native_read_cr3 ( ) = = trampoline_pgtable ) {
2018-05-16 11:01:29 +03:00
memcpy ( pgtable , trampoline_pgtable , PAGE_SIZE ) ;
native_write_cr3 ( ( unsigned long ) pgtable ) ;
2018-02-26 21:04:51 +03:00
}
2018-02-26 21:04:49 +03:00
/* Restore trampoline memory */
memcpy ( trampoline_32bit , trampoline_save , TRAMPOLINE_32BIT_SIZE ) ;
2020-10-10 15:11:10 -04:00
/* 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
2018-02-26 21:04:49 +03:00
}