1#include <asm/processor.h>
2#include "pgtable.h"
3#include "../string.h"
4
5
6
7
8
9
10
11
12unsigned long __force_order;
13
14#define BIOS_START_MIN 0x20000U
15#define BIOS_START_MAX 0x9f000U
16
17struct paging_config {
18 unsigned long trampoline_start;
19 unsigned long l5_required;
20};
21
22
23static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
24
25
26
27
28
29
30
31
32unsigned long *trampoline_32bit __section(.data);
33
34struct paging_config paging_prepare(void)
35{
36 struct paging_config paging_config = {};
37 unsigned long bios_start, ebda_start;
38
39
40
41
42
43
44
45
46
47
48
49
50 if (IS_ENABLED(CONFIG_X86_5LEVEL) &&
51 native_cpuid_eax(0) >= 7 &&
52 (native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) {
53 paging_config.l5_required = 1;
54 }
55
56
57
58
59
60
61 ebda_start = *(unsigned short *)0x40e << 4;
62 bios_start = *(unsigned short *)0x413 << 10;
63
64 if (bios_start < BIOS_START_MIN || bios_start > BIOS_START_MAX)
65 bios_start = BIOS_START_MAX;
66
67 if (ebda_start > BIOS_START_MIN && ebda_start < bios_start)
68 bios_start = ebda_start;
69
70
71 paging_config.trampoline_start = bios_start - TRAMPOLINE_32BIT_SIZE;
72 paging_config.trampoline_start = round_down(paging_config.trampoline_start, PAGE_SIZE);
73
74 trampoline_32bit = (unsigned long *)paging_config.trampoline_start;
75
76
77 memcpy(trampoline_save, trampoline_32bit, TRAMPOLINE_32BIT_SIZE);
78
79
80 memset(trampoline_32bit, 0, TRAMPOLINE_32BIT_SIZE);
81
82
83 memcpy(trampoline_32bit + TRAMPOLINE_32BIT_CODE_OFFSET / sizeof(unsigned long),
84 &trampoline_32bit_src, TRAMPOLINE_32BIT_CODE_SIZE);
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 if (paging_config.l5_required == !!(native_read_cr4() & X86_CR4_LA57))
101 goto out;
102
103 if (paging_config.l5_required) {
104
105
106
107
108 trampoline_32bit[TRAMPOLINE_32BIT_PGTABLE_OFFSET] = __native_read_cr3() | _PAGE_TABLE_NOENC;
109 } else {
110 unsigned long src;
111
112
113
114
115
116
117
118
119
120 src = *(unsigned long *)__native_read_cr3() & PAGE_MASK;
121 memcpy(trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET / sizeof(unsigned long),
122 (void *)src, PAGE_SIZE);
123 }
124
125out:
126 return paging_config;
127}
128
129void cleanup_trampoline(void *pgtable)
130{
131 void *trampoline_pgtable;
132
133 trampoline_pgtable = trampoline_32bit + TRAMPOLINE_32BIT_PGTABLE_OFFSET;
134
135
136
137
138
139 if ((void *)__native_read_cr3() == trampoline_pgtable) {
140 memcpy(pgtable, trampoline_pgtable, PAGE_SIZE);
141 native_write_cr3((unsigned long)pgtable);
142 }
143
144
145 memcpy(trampoline_32bit, trampoline_save, TRAMPOLINE_32BIT_SIZE);
146}
147