Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/boot/compressed/pgtable_64.c
50674 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include "misc.h"
3
#include <asm/bootparam.h>
4
#include <asm/bootparam_utils.h>
5
#include <asm/e820/types.h>
6
#include <asm/pgtable.h>
7
#include <asm/processor.h>
8
#include "../string.h"
9
#include "efi.h"
10
11
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
12
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
13
14
/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */
15
unsigned int __section(".data") __pgtable_l5_enabled;
16
unsigned int __section(".data") pgdir_shift = 39;
17
unsigned int __section(".data") ptrs_per_p4d = 1;
18
19
/* Buffer to preserve trampoline memory */
20
static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
21
22
/*
23
* Trampoline address will be printed by extract_kernel() for debugging
24
* purposes.
25
*
26
* Avoid putting the pointer into .bss as it will be cleared between
27
* configure_5level_paging() and extract_kernel().
28
*/
29
unsigned long *trampoline_32bit __section(".data");
30
31
int cmdline_find_option_bool(const char *option);
32
33
static unsigned long find_trampoline_placement(void)
34
{
35
unsigned long bios_start = 0, ebda_start = 0;
36
struct boot_e820_entry *entry;
37
char *signature;
38
int i;
39
40
/*
41
* Find a suitable spot for the trampoline.
42
* This code is based on reserve_bios_regions().
43
*/
44
45
/*
46
* EFI systems may not provide legacy ROM. The memory may not be mapped
47
* at all.
48
*
49
* Only look for values in the legacy ROM for non-EFI system.
50
*/
51
signature = (char *)&boot_params_ptr->efi_info.efi_loader_signature;
52
if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) &&
53
strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) {
54
ebda_start = *(unsigned short *)0x40e << 4;
55
bios_start = *(unsigned short *)0x413 << 10;
56
}
57
58
if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)
59
bios_start = BIOS_START_MAX;
60
61
if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)
62
bios_start = ebda_start;
63
64
bios_start = round_down(bios_start, PAGE_SIZE);
65
66
/* Find the first usable memory region under bios_start. */
67
for (i = boot_params_ptr->e820_entries - 1; i >= 0; i--) {
68
unsigned long new = bios_start;
69
70
entry = &boot_params_ptr->e820_table[i];
71
72
/* Skip all entries above bios_start. */
73
if (bios_start <= entry->addr)
74
continue;
75
76
/* Skip non-RAM entries. */
77
if (entry->type != E820_TYPE_RAM)
78
continue;
79
80
/* Adjust bios_start to the end of the entry if needed. */
81
if (bios_start > entry->addr + entry->size)
82
new = entry->addr + entry->size;
83
84
/* Keep bios_start page-aligned. */
85
new = round_down(new, PAGE_SIZE);
86
87
/* Skip the entry if it's too small. */
88
if (new - TRAMPOLINE_32BIT_SIZE < entry->addr)
89
continue;
90
91
/* Protect against underflow. */
92
if (new - TRAMPOLINE_32BIT_SIZE > bios_start)
93
break;
94
95
bios_start = new;
96
break;
97
}
98
99
/* Place the trampoline just below the end of low memory */
100
return bios_start - TRAMPOLINE_32BIT_SIZE;
101
}
102
103
asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
104
{
105
void (*toggle_la57)(void *cr3);
106
bool l5_required = false;
107
108
/* Initialize boot_params. Required for cmdline_find_option_bool(). */
109
sanitize_boot_params(bp);
110
boot_params_ptr = bp;
111
112
/*
113
* Check if LA57 is desired and supported.
114
*
115
* There are several parts to the check:
116
* - if user asked to disable 5-level paging: no5lvl in cmdline
117
* - if the machine supports 5-level paging:
118
* + CPUID leaf 7 is supported
119
* + the leaf has the feature bit set
120
*/
121
if (!cmdline_find_option_bool("no5lvl") &&
122
native_cpuid_eax(0) >= 7 && (native_cpuid_ecx(7) & BIT(16))) {
123
l5_required = true;
124
125
/* Initialize variables for 5-level paging */
126
__pgtable_l5_enabled = 1;
127
pgdir_shift = 48;
128
ptrs_per_p4d = 512;
129
}
130
131
/*
132
* The trampoline will not be used if the paging mode is already set to
133
* the desired one.
134
*/
135
if (l5_required == !!(native_read_cr4() & X86_CR4_LA57))
136
return;
137
138
trampoline_32bit = (unsigned long *)find_trampoline_placement();
139
140
/* Preserve trampoline memory */
141
memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE);
142
143
/* Clear trampoline memory first */
144
memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE);
145
146
/* Copy trampoline code in place */
147
toggle_la57 = memcpy(trampoline_32bit +
148
TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long),
149
&trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE);
150
151
/*
152
* Avoid the need for a stack in the 32-bit trampoline code, by using
153
* LJMP rather than LRET to return back to long mode. LJMP takes an
154
* immediate absolute address, which needs to be adjusted based on the
155
* placement of the trampoline.
156
*/
157
*(u32 *)((u8 *)toggle_la57 + trampoline_ljmp_imm_offset) +=
158
(unsigned long)toggle_la57;
159
160
/*
161
* The code below prepares page table in trampoline memory.
162
*
163
* The new page table will be used by trampoline code for switching
164
* from 4- to 5-level paging or vice versa.
165
*/
166
167
if (l5_required) {
168
/*
169
* For 4- to 5-level paging transition, set up current CR3 as
170
* the first and the only entry in a new top-level page table.
171
*/
172
*trampoline_32bit = native_read_cr3_pa() | _PAGE_TABLE_NOENC;
173
} else {
174
u64 *new_cr3;
175
pgd_t *pgdp;
176
177
/*
178
* For 5- to 4-level paging transition, copy page table pointed
179
* by first entry in the current top-level page table as our
180
* new top-level page table.
181
*
182
* We cannot just point to the page table from trampoline as it
183
* may be above 4G.
184
*/
185
pgdp = (pgd_t *)native_read_cr3_pa();
186
new_cr3 = (u64 *)(native_pgd_val(pgdp[0]) & PTE_PFN_MASK);
187
memcpy(trampoline_32bit, new_cr3, PAGE_SIZE);
188
}
189
190
toggle_la57(trampoline_32bit);
191
192
/*
193
* Move the top level page table out of trampoline memory.
194
*/
195
memcpy(pgtable, trampoline_32bit, PAGE_SIZE);
196
native_write_cr3((unsigned long)pgtable);
197
198
/* Restore trampoline memory */
199
memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE);
200
}
201
202