]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/X64/AmdSev.nasm
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / X64 / AmdSev.nasm
CommitLineData
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
18RegisterGhcbGpa:\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
53GhcbGpaRegisterFailure:\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
61DoHltLoop:\r
62 cli\r
63 hlt\r
64 jmp DoHltLoop\r
65\r
66RegisterGhcbGpaDone:\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
73SevEsSetupGhcb:\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
98SevEsSetupGhcbExit:\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
105SevEsGetApicId:\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
146CheckExtTopoAvail:\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 165GetApicIdSevEs:\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
176NoX2ApicSevEs:\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
186RestoreGhcb:\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
199SevEsGetApicIdExit:\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
213SwitchToRealProcStart:\r
214BITS 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
279BITS 32\r
280CompatMode:\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
316BITS 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
329PM16Mode:\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
348SwitchToRealProcEnd:\r
c7c25997
XY
349\r
350;-------------------------------------------------------------------------------------\r
351; AsmRelocateApLoopAmdSev (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer);\r
352;-------------------------------------------------------------------------------------\r
353\r
354AsmRelocateApLoopAmdSevStart:\r
355BITS 64\r
356 cmp qword [rsp + 56], 0 ; SevEsAPJumpTable\r
357 je NoSevEsAmdSev\r
358\r
359 ;\r
360 ; Perform some SEV-ES related setup before leaving 64-bit mode\r
361 ;\r
362 push rcx\r
363 push rdx\r
364\r
365 ;\r
366 ; Get the RDX reset value using CPUID\r
367 ;\r
368 mov rax, 1\r
369 cpuid\r
370 mov rsi, rax ; Save off the reset value for RDX\r
371\r
372 ;\r
373 ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call\r
374 ; - Must be done while in 64-bit long mode so that writes to\r
375 ; the GHCB memory will be unencrypted.\r
376 ; - No NAE events can be generated once this is set otherwise\r
377 ; the AP_RESET_HOLD SW_EXITCODE will be overwritten.\r
378 ;\r
379 mov rcx, 0xc0010130\r
380 rdmsr ; Retrieve current GHCB address\r
381 shl rdx, 32\r
382 or rdx, rax\r
383\r
384 mov rdi, rdx\r
385 xor rax, rax\r
386 mov rcx, 0x800\r
387 shr rcx, 3\r
388 rep stosq ; Clear the GHCB\r
389\r
390 mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD\r
391 mov [rdx + 0x390], rax\r
392 mov rax, 114 ; Set SwExitCode valid bit\r
393 bts [rdx + 0x3f0], rax\r
394 inc rax ; Set SwExitInfo1 valid bit\r
395 bts [rdx + 0x3f0], rax\r
396 inc rax ; Set SwExitInfo2 valid bit\r
397 bts [rdx + 0x3f0], rax\r
398\r
399 pop rdx\r
400 pop rcx\r
401\r
402NoSevEsAmdSev:\r
403 cli ; Disable interrupt before switching to 32-bit mode\r
404 mov rax, [rsp + 40] ; CountTofinish\r
405 lock dec dword [rax] ; (*CountTofinish)--\r
406\r
407 mov r10, [rsp + 48] ; Pm16CodeSegment\r
408 mov rax, [rsp + 56] ; SevEsAPJumpTable\r
409 mov rbx, [rsp + 64] ; WakeupBuffer\r
410 mov rsp, r9 ; TopOfApStack\r
411\r
412 push rax ; Save SevEsAPJumpTable\r
413 push rbx ; Save WakeupBuffer\r
414 push r10 ; Save Pm16CodeSegment\r
415 push rcx ; Save MwaitSupport\r
416 push rdx ; Save ApTargetCState\r
417\r
418 lea rax, [PmEntryAmdSev] ; rax <- The start address of transition code\r
419\r
420 push r8\r
421 push rax\r
422\r
423 ;\r
424 ; Clear R8 - R15, for reset, before going into 32-bit mode\r
425 ;\r
426 xor r8, r8\r
427 xor r9, r9\r
428 xor r10, r10\r
429 xor r11, r11\r
430 xor r12, r12\r
431 xor r13, r13\r
432 xor r14, r14\r
433 xor r15, r15\r
434\r
435 ;\r
436 ; Far return into 32-bit mode\r
437 ;\r
438o64 retf\r
439\r
440BITS 32\r
441PmEntryAmdSev:\r
442 mov eax, cr0\r
443 btr eax, 31 ; Clear CR0.PG\r
444 mov cr0, eax ; Disable paging and caches\r
445\r
446 mov ecx, 0xc0000080\r
447 rdmsr\r
448 and ah, ~ 1 ; Clear LME\r
449 wrmsr\r
450 mov eax, cr4\r
451 and al, ~ (1 << 5) ; Clear PAE\r
452 mov cr4, eax\r
453\r
454 pop edx\r
455 add esp, 4\r
456 pop ecx,\r
457 add esp, 4\r
458\r
459MwaitCheckAmdSev:\r
460 cmp cl, 1 ; Check mwait-monitor support\r
461 jnz HltLoopAmdSev\r
462 mov ebx, edx ; Save C-State to ebx\r
463MwaitLoopAmdSev:\r
464 cli\r
465 mov eax, esp ; Set Monitor Address\r
466 xor ecx, ecx ; ecx = 0\r
467 xor edx, edx ; edx = 0\r
468 monitor\r
469 mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]\r
470 shl eax, 4\r
471 mwait\r
472 jmp MwaitLoopAmdSev\r
473\r
474HltLoopAmdSev:\r
475 pop edx ; PM16CodeSegment\r
476 add esp, 4\r
477 pop ebx ; WakeupBuffer\r
478 add esp, 4\r
479 pop eax ; SevEsAPJumpTable\r
480 add esp, 4\r
481 cmp eax, 0 ; Check for SEV-ES\r
482 je DoHltAmdSev\r
483\r
484 cli\r
485 ;\r
486 ; SEV-ES is enabled, use VMGEXIT (GHCB information already\r
487 ; set by caller)\r
488 ;\r
489BITS 64\r
490 rep vmmcall\r
491BITS 32\r
492\r
493 ;\r
494 ; Back from VMGEXIT AP_HLT_LOOP\r
495 ; Push the FLAGS/CS/IP values to use\r
496 ;\r
497 push word 0x0002 ; EFLAGS\r
498 xor ecx, ecx\r
499 mov cx, [eax + 2] ; CS\r
500 push cx\r
501 mov cx, [eax] ; IP\r
502 push cx\r
503 push word 0x0000 ; For alignment, will be discarded\r
504\r
505 push edx\r
506 push ebx\r
507\r
508 mov edx, esi ; Restore RDX reset value\r
509\r
510 retf\r
511\r
512DoHltAmdSev:\r
513 cli\r
514 hlt\r
515 jmp DoHltAmdSev\r
516\r
517BITS 64\r
518AsmRelocateApLoopAmdSevEnd:\r