1#include <linux/efi.h>
2#include <asm/e820/types.h>
3#include <asm/processor.h>
4#include <asm/efi.h>
5#include "pgtable.h"
6#include "../string.h"
7
8#define BIOS_START_MIN 0x20000U
9#define BIOS_START_MAX 0x9f000U
10
11#ifdef CONFIG_X86_5LEVEL
12
13unsigned int __section(".data") __pgtable_l5_enabled;
14unsigned int __section(".data") pgdir_shift = 39;
15unsigned int __section(".data") ptrs_per_p4d = 1;
16#endif
17
18struct paging_config {
19 unsigned long trampoline_start;
20 unsigned long l5_required;
21};
22
23
24static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
25
26
27
28
29
30
31
32
33unsigned long *trampoline_32bit __section(".data");
34
35extern struct boot_params *boot_params;
36int cmdline_find_option_bool(const char *option);
37
38static unsigned long find_trampoline_placement(void)
39{
40 unsigned long bios_start = 0, ebda_start = 0;
41 struct boot_e820_entry *entry;
42 char *signature;
43 int i;
44
45
46
47
48
49
50
51
52
53
54
55
56 signature = (char *)&boot_params->efi_info.efi_loader_signature;
57 if (strncmp(signature, EFI32_LOADER_SIGNATURE, 4) &&
58 strncmp(signature, EFI64_LOADER_SIGNATURE, 4)) {
59 ebda_start = *(unsigned short *)0x40e << 4;
60 bios_start = *(unsigned short *)0x413 << 10;
61 }
62
63 if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)
64 bios_start = BIOS_START_MAX;
65
66 if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)
67 bios_start = ebda_start;
68
69 bios_start = round_down(bios_start, PAGE_SIZE);
70
71
72 for (i = boot_params->e820_entries - 1; i >= 0; i--) {
73 unsigned long new = bios_start;
74
75 entry = &boot_params->e820_table[i];
76
77
78 if (bios_start <= entry->addr)
79 continue;
80
81
82 if (entry->type != E820_TYPE_RAM)
83 continue;
84
85
86 if (bios_start > entry->addr + entry->size)
87 new = entry->addr + entry->size;
88
89
90 new = round_down(new, PAGE_SIZE);
91
92
93 if (new - TRAMPOLINE_32BIT_SIZE < entry->addr)
94 continue;
95
96
97 if (new - TRAMPOLINE_32BIT_SIZE > bios_start)
98 break;
99
100 bios_start = new;
101 break;
102 }
103
104
105 return bios_start - TRAMPOLINE_32BIT_SIZE;
106}
107
108struct paging_config paging_prepare(void *rmode)
109{
110 struct paging_config paging_config = {};
111
112
113 boot_params = rmode;
114
115
116
117
118
119
120
121
122
123
124
125
126
127 if (IS_ENABLED(CONFIG_X86_5LEVEL) &&
128 !cmdline_find_option_bool("no5lvl") &&
129 native_cpuid_eax(0) >= 7 &&
130 (native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) {
131 paging_config.l5_required = 1;
132 }
133
134 paging_config.trampoline_start = find_trampoline_placement();
135
136 trampoline_32bit = (unsigned long *)paging_config.trampoline_start;
137
138
139 memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE);
140
141
142 memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE);
143
144
145 memcpy(trampoline_32bit + TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long),
146 &trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE);
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 if (paging_config.l5_required == !!(native_read_cr4() & X86_CR4_LA57))
163 goto out;
164
165 if (paging_config.l5_required) {
166
167
168
169
170 trampoline_32bit[TRAMPOLINE_32BIT_PGTABLE_OFFSET] = __native_read_cr3() | _PAGE_TABLE_NOENC;
171 } else {
172 unsigned long src;
173
174
175
176
177
178
179
180
181
182 src = *(unsigned long *)__native_read_cr3() & PAGE_MASK;
183 memcpy(trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof(unsigned long),
184 (void *)src, PAGE_SIZE);
185 }
186
187out:
188 return paging_config;
189}
190
191void cleanup_trampoline(void *pgtable)
192{
193 void *trampoline_pgtable;
194
195 trampoline_pgtable = trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof(unsigned long);
196
197
198
199
200
201 if ((void *)__native_read_cr3() == trampoline_pgtable) {
202 memcpy(pgtable, trampoline_pgtable, PAGE_SIZE);
203 native_write_cr3((unsigned long)pgtable);
204 }
205
206
207 memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE);
208
209
210#ifdef CONFIG_X86_5LEVEL
211 if (__read_cr4() & X86_CR4_LA57) {
212 __pgtable_l5_enabled = 1;
213 pgdir_shift = 48;
214 ptrs_per_p4d = 512;
215 }
216#endif
217}
218