]>
Commit | Line | Data |
---|---|---|
e2289d19 BS |
1 | ;------------------------------------------------------------------------------ ;\r |
2 | ; Copyright (c) 2021, AMD Inc. All rights reserved.<BR>\r | |
3 | ; SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
4 | ;\r | |
5 | ; Module Name:\r | |
6 | ;\r | |
7 | ; AmdSev.nasm\r | |
8 | ;\r | |
9 | ; Abstract:\r | |
10 | ;\r | |
11 | ; This provides helper used by the MpFunc.nasm. If AMD SEV-ES is active\r | |
12 | ; then helpers perform the additional setups (such as GHCB).\r | |
13 | ;\r | |
14 | ;-------------------------------------------------------------------------------\r | |
15 | \r | |
16 | %define SIZE_4KB 0x1000\r | |
17 | \r | |
9c703bc0 BS |
18 | RegisterGhcbGpa:\r |
19 | ;\r | |
20 | ; Register GHCB GPA when SEV-SNP is enabled\r | |
21 | ;\r | |
22 | lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]\r | |
23 | cmp byte [edi], 1 ; SevSnpIsEnabled\r | |
24 | jne RegisterGhcbGpaDone\r | |
25 | \r | |
26 | ; Save the rdi and rsi to used for later comparison\r | |
27 | push rdi\r | |
28 | push rsi\r | |
29 | mov edi, eax\r | |
30 | mov esi, edx\r | |
31 | or eax, 18 ; Ghcb registration request\r | |
32 | wrmsr\r | |
33 | rep vmmcall\r | |
34 | rdmsr\r | |
35 | mov r12, rax\r | |
36 | and r12, 0fffh\r | |
37 | cmp r12, 19 ; Ghcb registration response\r | |
38 | jne GhcbGpaRegisterFailure\r | |
39 | \r | |
40 | ; Verify that GPA is not changed\r | |
41 | and eax, 0fffff000h\r | |
42 | cmp edi, eax\r | |
43 | jne GhcbGpaRegisterFailure\r | |
44 | cmp esi, edx\r | |
45 | jne GhcbGpaRegisterFailure\r | |
46 | pop rsi\r | |
47 | pop rdi\r | |
48 | jmp RegisterGhcbGpaDone\r | |
49 | \r | |
50 | ;\r | |
51 | ; Request the guest termination\r | |
52 | ;\r | |
53 | GhcbGpaRegisterFailure:\r | |
54 | xor edx, edx\r | |
55 | mov eax, 256 ; GHCB terminate\r | |
56 | wrmsr\r | |
57 | rep vmmcall\r | |
58 | \r | |
59 | ; We should not return from the above terminate request, but if we do\r | |
60 | ; then enter into the hlt loop.\r | |
61 | DoHltLoop:\r | |
62 | cli\r | |
63 | hlt\r | |
64 | jmp DoHltLoop\r | |
65 | \r | |
66 | RegisterGhcbGpaDone:\r | |
67 | OneTimeCallRet RegisterGhcbGpa\r | |
68 | \r | |
e2289d19 BS |
69 | ;\r |
70 | ; The function checks whether SEV-ES is enabled, if enabled\r | |
71 | ; then setup the GHCB page.\r | |
72 | ;\r | |
73 | SevEsSetupGhcb:\r | |
74 | lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r | |
75 | cmp byte [edi], 1 ; SevEsIsEnabled\r | |
76 | jne SevEsSetupGhcbExit\r | |
77 | \r | |
78 | ;\r | |
79 | ; program GHCB\r | |
80 | ; Each page after the GHCB is a per-CPU page, so the calculation programs\r | |
81 | ; a GHCB to be every 8KB.\r | |
82 | ;\r | |
83 | mov eax, SIZE_4KB\r | |
84 | shl eax, 1 ; EAX = SIZE_4K * 2\r | |
85 | mov ecx, ebx\r | |
86 | mul ecx ; EAX = SIZE_4K * 2 * CpuNumber\r | |
87 | mov edi, esi\r | |
88 | add edi, MP_CPU_EXCHANGE_INFO_FIELD (GhcbBase)\r | |
89 | add rax, qword [edi]\r | |
90 | mov rdx, rax\r | |
91 | shr rdx, 32\r | |
92 | mov rcx, 0xc0010130\r | |
9c703bc0 BS |
93 | \r |
94 | OneTimeCall RegisterGhcbGpa\r | |
95 | \r | |
e2289d19 BS |
96 | wrmsr\r |
97 | \r | |
98 | SevEsSetupGhcbExit:\r | |
99 | OneTimeCallRet SevEsSetupGhcb\r | |
100 | \r | |
101 | ;\r | |
102 | ; The function checks whether SEV-ES is enabled, if enabled, use\r | |
103 | ; the GHCB\r | |
104 | ;\r | |
105 | SevEsGetApicId:\r | |
106 | lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]\r | |
107 | cmp byte [edi], 1 ; SevEsIsEnabled\r | |
108 | jne SevEsGetApicIdExit\r | |
109 | \r | |
110 | ;\r | |
111 | ; Since we don't have a stack yet, we can't take a #VC\r | |
112 | ; exception. Use the GHCB protocol to perform the CPUID\r | |
113 | ; calls.\r | |
114 | ;\r | |
115 | mov rcx, 0xc0010130\r | |
116 | rdmsr\r | |
117 | shl rdx, 32\r | |
118 | or rax, rdx\r | |
119 | mov rdi, rax ; RDI now holds the original GHCB GPA\r | |
120 | \r | |
d4d7c9ad MR |
121 | ;\r |
122 | ; For SEV-SNP, the recommended handling for getting the x2APIC ID\r | |
123 | ; would be to use the SNP CPUID table to fetch CPUID.00H:EAX and\r | |
124 | ; CPUID:0BH:EBX[15:0] instead of the GHCB MSR protocol vmgexits\r | |
125 | ; below.\r | |
126 | ;\r | |
127 | ; To avoid the unecessary ugliness to accomplish that here, the BSP\r | |
128 | ; has performed these checks in advance (where #VC handler handles\r | |
129 | ; the CPUID table lookups automatically) and cached them in a flag\r | |
130 | ; so those checks can be skipped here.\r | |
131 | ;\r | |
132 | mov eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]\r | |
133 | cmp al, 1\r | |
134 | jne CheckExtTopoAvail\r | |
135 | \r | |
136 | ;\r | |
137 | ; Even with SEV-SNP, the actual x2APIC ID in CPUID.0BH:EDX\r | |
138 | ; fetched from the hypervisor the same way SEV-ES does it.\r | |
139 | ;\r | |
140 | mov eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (ExtTopoAvail)]\r | |
141 | cmp al, 1\r | |
142 | je GetApicIdSevEs\r | |
143 | ; The 8-bit APIC ID fallback is also the same as with SEV-ES\r | |
144 | jmp NoX2ApicSevEs\r | |
145 | \r | |
146 | CheckExtTopoAvail:\r | |
e2289d19 BS |
147 | mov rdx, 0 ; CPUID function 0\r |
148 | mov rax, 0 ; RAX register requested\r | |
149 | or rax, 4\r | |
150 | wrmsr\r | |
151 | rep vmmcall\r | |
152 | rdmsr\r | |
153 | cmp edx, 0bh\r | |
154 | jb NoX2ApicSevEs ; CPUID level below CPUID_EXTENDED_TOPOLOGY\r | |
155 | \r | |
156 | mov rdx, 0bh ; CPUID function 0x0b\r | |
157 | mov rax, 040000000h ; RBX register requested\r | |
158 | or rax, 4\r | |
159 | wrmsr\r | |
160 | rep vmmcall\r | |
161 | rdmsr\r | |
162 | test edx, 0ffffh\r | |
163 | jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero\r | |
164 | \r | |
d4d7c9ad | 165 | GetApicIdSevEs:\r |
e2289d19 BS |
166 | mov rdx, 0bh ; CPUID function 0x0b\r |
167 | mov rax, 0c0000000h ; RDX register requested\r | |
168 | or rax, 4\r | |
169 | wrmsr\r | |
170 | rep vmmcall\r | |
171 | rdmsr\r | |
172 | \r | |
173 | ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX\r | |
174 | jmp RestoreGhcb\r | |
175 | \r | |
176 | NoX2ApicSevEs:\r | |
177 | ; Processor is not x2APIC capable, so get 8-bit APIC ID\r | |
178 | mov rdx, 1 ; CPUID function 1\r | |
179 | mov rax, 040000000h ; RBX register requested\r | |
180 | or rax, 4\r | |
181 | wrmsr\r | |
182 | rep vmmcall\r | |
183 | rdmsr\r | |
184 | shr edx, 24\r | |
185 | \r | |
186 | RestoreGhcb:\r | |
187 | mov rbx, rdx ; Save x2APIC/APIC ID\r | |
188 | \r | |
189 | mov rdx, rdi ; RDI holds the saved GHCB GPA\r | |
190 | shr rdx, 32\r | |
191 | mov eax, edi\r | |
192 | wrmsr\r | |
193 | \r | |
194 | mov rdx, rbx\r | |
195 | \r | |
196 | ; x2APIC ID or APIC ID is in EDX\r | |
197 | jmp GetProcessorNumber\r | |
198 | \r | |
199 | SevEsGetApicIdExit:\r | |
200 | OneTimeCallRet SevEsGetApicId\r | |
b4d7b9d2 RN |
201 | \r |
202 | \r | |
203 | ;-------------------------------------------------------------------------------------\r | |
204 | ;SwitchToRealProc procedure follows.\r | |
205 | ;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC\r | |
206 | ;IS IN MACHINE CODE.\r | |
207 | ; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart)\r | |
208 | ; rcx - Buffer Start\r | |
209 | ; rdx - Code16 Selector Offset\r | |
210 | ; r8 - Code32 Selector Offset\r | |
211 | ; r9 - Stack Start\r | |
212 | ;-------------------------------------------------------------------------------------\r | |
213 | SwitchToRealProcStart:\r | |
214 | BITS 64\r | |
215 | cli\r | |
216 | \r | |
217 | ;\r | |
218 | ; Get RDX reset value before changing stacks since the\r | |
219 | ; new stack won't be able to accomodate a #VC exception.\r | |
220 | ;\r | |
221 | push rax\r | |
222 | push rbx\r | |
223 | push rcx\r | |
224 | push rdx\r | |
225 | \r | |
226 | mov rax, 1\r | |
227 | cpuid\r | |
228 | mov rsi, rax ; Save off the reset value for RDX\r | |
229 | \r | |
230 | pop rdx\r | |
231 | pop rcx\r | |
232 | pop rbx\r | |
233 | pop rax\r | |
234 | \r | |
235 | ;\r | |
236 | ; Establish stack below 1MB\r | |
237 | ;\r | |
238 | mov rsp, r9\r | |
239 | \r | |
240 | ;\r | |
241 | ; Push ultimate Reset Vector onto the stack\r | |
242 | ;\r | |
243 | mov rax, rcx\r | |
244 | shr rax, 4\r | |
245 | push word 0x0002 ; RFLAGS\r | |
246 | push ax ; CS\r | |
247 | push word 0x0000 ; RIP\r | |
248 | push word 0x0000 ; For alignment, will be discarded\r | |
249 | \r | |
250 | ;\r | |
251 | ; Get address of "16-bit operand size" label\r | |
252 | ;\r | |
253 | lea rbx, [PM16Mode]\r | |
254 | \r | |
255 | ;\r | |
256 | ; Push addresses used to change to compatibility mode\r | |
257 | ;\r | |
258 | lea rax, [CompatMode]\r | |
259 | push r8\r | |
260 | push rax\r | |
261 | \r | |
262 | ;\r | |
263 | ; Clear R8 - R15, for reset, before going into 32-bit mode\r | |
264 | ;\r | |
265 | xor r8, r8\r | |
266 | xor r9, r9\r | |
267 | xor r10, r10\r | |
268 | xor r11, r11\r | |
269 | xor r12, r12\r | |
270 | xor r13, r13\r | |
271 | xor r14, r14\r | |
272 | xor r15, r15\r | |
273 | \r | |
274 | ;\r | |
275 | ; Far return into 32-bit mode\r | |
276 | ;\r | |
277 | retfq\r | |
278 | \r | |
279 | BITS 32\r | |
280 | CompatMode:\r | |
281 | ;\r | |
282 | ; Set up stack to prepare for exiting protected mode\r | |
283 | ;\r | |
284 | push edx ; Code16 CS\r | |
285 | push ebx ; PM16Mode label address\r | |
286 | \r | |
287 | ;\r | |
288 | ; Disable paging\r | |
289 | ;\r | |
290 | mov eax, cr0 ; Read CR0\r | |
291 | btr eax, 31 ; Set PG=0\r | |
292 | mov cr0, eax ; Write CR0\r | |
293 | \r | |
294 | ;\r | |
295 | ; Disable long mode\r | |
296 | ;\r | |
297 | mov ecx, 0c0000080h ; EFER MSR number\r | |
298 | rdmsr ; Read EFER\r | |
299 | btr eax, 8 ; Set LME=0\r | |
300 | wrmsr ; Write EFER\r | |
301 | \r | |
302 | ;\r | |
303 | ; Disable PAE\r | |
304 | ;\r | |
305 | mov eax, cr4 ; Read CR4\r | |
306 | btr eax, 5 ; Set PAE=0\r | |
307 | mov cr4, eax ; Write CR4\r | |
308 | \r | |
309 | mov edx, esi ; Restore RDX reset value\r | |
310 | \r | |
311 | ;\r | |
312 | ; Switch to 16-bit operand size\r | |
313 | ;\r | |
314 | retf\r | |
315 | \r | |
316 | BITS 16\r | |
317 | ;\r | |
318 | ; At entry to this label\r | |
319 | ; - RDX will have its reset value\r | |
320 | ; - On the top of the stack\r | |
321 | ; - Alignment data (two bytes) to be discarded\r | |
322 | ; - IP for Real Mode (two bytes)\r | |
323 | ; - CS for Real Mode (two bytes)\r | |
324 | ;\r | |
325 | ; This label is also used with AsmRelocateApLoop. During MP finalization,\r | |
326 | ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of\r | |
327 | ; the WakeupBuffer, allowing a parked AP to be booted by an OS.\r | |
328 | ;\r | |
329 | PM16Mode:\r | |
330 | mov eax, cr0 ; Read CR0\r | |
331 | btr eax, 0 ; Set PE=0\r | |
332 | mov cr0, eax ; Write CR0\r | |
333 | \r | |
334 | pop ax ; Discard alignment data\r | |
335 | \r | |
336 | ;\r | |
337 | ; Clear registers (except RDX and RSP) before going into 16-bit mode\r | |
338 | ;\r | |
339 | xor eax, eax\r | |
340 | xor ebx, ebx\r | |
341 | xor ecx, ecx\r | |
342 | xor esi, esi\r | |
343 | xor edi, edi\r | |
344 | xor ebp, ebp\r | |
345 | \r | |
346 | iret\r | |
347 | \r | |
348 | SwitchToRealProcEnd:\r |