1 ;------------------------------------------------------------------------------ ;
2 ; Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
3 ; SPDX-License-Identifier: BSD-2-Clause-Patent
11 ; This provides helper used by the MpFunc.nasm. If AMD SEV-ES is active
12 ; then helpers perform the additional setups (such as GHCB).
14 ;-------------------------------------------------------------------------------
16 %define SIZE_4KB 0x1000
20 ; Register GHCB GPA when SEV-SNP is enabled
22 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]
23 cmp byte [edi], 1 ; SevSnpIsEnabled
24 jne RegisterGhcbGpaDone
26 ; Save the rdi and rsi to used for later comparison
31 or eax, 18 ; Ghcb registration request
37 cmp r12, 19 ; Ghcb registration response
38 jne GhcbGpaRegisterFailure
40 ; Verify that GPA is not changed
43 jne GhcbGpaRegisterFailure
45 jne GhcbGpaRegisterFailure
48 jmp RegisterGhcbGpaDone
51 ; Request the guest termination
53 GhcbGpaRegisterFailure:
55 mov eax, 256 ; GHCB terminate
59 ; We should not return from the above terminate request, but if we do
60 ; then enter into the hlt loop.
67 OneTimeCallRet RegisterGhcbGpa
70 ; The function checks whether SEV-ES is enabled, if enabled
71 ; then setup the GHCB page.
74 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]
75 cmp byte [edi], 1 ; SevEsIsEnabled
76 jne SevEsSetupGhcbExit
80 ; Each page after the GHCB is a per-CPU page, so the calculation programs
81 ; a GHCB to be every 8KB.
84 shl eax, 1 ; EAX = SIZE_4K * 2
86 mul ecx ; EAX = SIZE_4K * 2 * CpuNumber
88 add edi, MP_CPU_EXCHANGE_INFO_FIELD (GhcbBase)
94 OneTimeCall RegisterGhcbGpa
99 OneTimeCallRet SevEsSetupGhcb
102 ; The function checks whether SEV-ES is enabled, if enabled, use
106 lea edi, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevEsIsEnabled)]
107 cmp byte [edi], 1 ; SevEsIsEnabled
108 jne SevEsGetApicIdExit
111 ; Since we don't have a stack yet, we can't take a #VC
112 ; exception. Use the GHCB protocol to perform the CPUID
119 mov rdi, rax ; RDI now holds the original GHCB GPA
122 ; For SEV-SNP, the recommended handling for getting the x2APIC ID
123 ; would be to use the SNP CPUID table to fetch CPUID.00H:EAX and
124 ; CPUID:0BH:EBX[15:0] instead of the GHCB MSR protocol vmgexits
127 ; To avoid the unecessary ugliness to accomplish that here, the BSP
128 ; has performed these checks in advance (where #VC handler handles
129 ; the CPUID table lookups automatically) and cached them in a flag
130 ; so those checks can be skipped here.
132 mov eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (SevSnpIsEnabled)]
134 jne CheckExtTopoAvail
137 ; Even with SEV-SNP, the actual x2APIC ID in CPUID.0BH:EDX
138 ; fetched from the hypervisor the same way SEV-ES does it.
140 mov eax, [esi + MP_CPU_EXCHANGE_INFO_FIELD (ExtTopoAvail)]
143 ; The 8-bit APIC ID fallback is also the same as with SEV-ES
147 mov rdx, 0 ; CPUID function 0
148 mov rax, 0 ; RAX register requested
154 jb NoX2ApicSevEs ; CPUID level below CPUID_EXTENDED_TOPOLOGY
156 mov rdx, 0bh ; CPUID function 0x0b
157 mov rax, 040000000h ; RBX register requested
163 jz NoX2ApicSevEs ; CPUID.0BH:EBX[15:0] is zero
166 mov rdx, 0bh ; CPUID function 0x0b
167 mov rax, 0c0000000h ; RDX register requested
173 ; Processor is x2APIC capable; 32-bit x2APIC ID is now in EDX
177 ; Processor is not x2APIC capable, so get 8-bit APIC ID
178 mov rdx, 1 ; CPUID function 1
179 mov rax, 040000000h ; RBX register requested
187 mov rbx, rdx ; Save x2APIC/APIC ID
189 mov rdx, rdi ; RDI holds the saved GHCB GPA
196 ; x2APIC ID or APIC ID is in EDX
197 jmp GetProcessorNumber
200 OneTimeCallRet SevEsGetApicId
203 ;-------------------------------------------------------------------------------------
204 ;SwitchToRealProc procedure follows.
205 ;ALSO THIS PROCEDURE IS EXECUTED BY APs TRANSITIONING TO 16 BIT MODE. HENCE THIS PROC
207 ; SwitchToRealProc (UINTN BufferStart, UINT16 Code16, UINT16 Code32, UINTN StackStart)
209 ; rdx - Code16 Selector Offset
210 ; r8 - Code32 Selector Offset
212 ;-------------------------------------------------------------------------------------
213 SwitchToRealProcStart:
218 ; Get RDX reset value before changing stacks since the
219 ; new stack won't be able to accomodate a #VC exception.
228 mov rsi, rax ; Save off the reset value for RDX
236 ; Establish stack below 1MB
241 ; Push ultimate Reset Vector onto the stack
245 push word 0x0002 ; RFLAGS
247 push word 0x0000 ; RIP
248 push word 0x0000 ; For alignment, will be discarded
251 ; Get address of "16-bit operand size" label
256 ; Push addresses used to change to compatibility mode
258 lea rax, [CompatMode]
263 ; Clear R8 - R15, for reset, before going into 32-bit mode
275 ; Far return into 32-bit mode
282 ; Set up stack to prepare for exiting protected mode
285 push ebx ; PM16Mode label address
290 mov eax, cr0 ; Read CR0
291 btr eax, 31 ; Set PG=0
292 mov cr0, eax ; Write CR0
297 mov ecx, 0c0000080h ; EFER MSR number
299 btr eax, 8 ; Set LME=0
305 mov eax, cr4 ; Read CR4
306 btr eax, 5 ; Set PAE=0
307 mov cr4, eax ; Write CR4
309 mov edx, esi ; Restore RDX reset value
312 ; Switch to 16-bit operand size
318 ; At entry to this label
319 ; - RDX will have its reset value
320 ; - On the top of the stack
321 ; - Alignment data (two bytes) to be discarded
322 ; - IP for Real Mode (two bytes)
323 ; - CS for Real Mode (two bytes)
325 ; This label is also used with AsmRelocateApLoop. During MP finalization,
326 ; the code from PM16Mode to SwitchToRealProcEnd is copied to the start of
327 ; the WakeupBuffer, allowing a parked AP to be booted by an OS.
330 mov eax, cr0 ; Read CR0
331 btr eax, 0 ; Set PE=0
332 mov cr0, eax ; Write CR0
334 pop ax ; Discard alignment data
337 ; Clear registers (except RDX and RSP) before going into 16-bit mode
349 ;-------------------------------------------------------------------------------------
350 ; AsmRelocateApLoopAmd (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish, Pm16CodeSegment, SevEsAPJumpTable, WakeupBuffer);
351 ;-------------------------------------------------------------------------------------
353 AsmRelocateApLoopStartAmd:
355 cmp qword [rsp + 56], 0 ; SevEsAPJumpTable
359 ; Perform some SEV-ES related setup before leaving 64-bit mode
365 ; Get the RDX reset value using CPUID
369 mov rsi, rax ; Save off the reset value for RDX
372 ; Prepare the GHCB for the AP_HLT_LOOP VMGEXIT call
373 ; - Must be done while in 64-bit long mode so that writes to
374 ; the GHCB memory will be unencrypted.
375 ; - No NAE events can be generated once this is set otherwise
376 ; the AP_RESET_HOLD SW_EXITCODE will be overwritten.
379 rdmsr ; Retrieve current GHCB address
387 rep stosq ; Clear the GHCB
389 mov rax, 0x80000004 ; VMGEXIT AP_RESET_HOLD
390 mov [rdx + 0x390], rax
391 mov rax, 114 ; Set SwExitCode valid bit
392 bts [rdx + 0x3f0], rax
393 inc rax ; Set SwExitInfo1 valid bit
394 bts [rdx + 0x3f0], rax
395 inc rax ; Set SwExitInfo2 valid bit
396 bts [rdx + 0x3f0], rax
402 cli ; Disable interrupt before switching to 32-bit mode
403 mov rax, [rsp + 40] ; CountTofinish
404 lock dec dword [rax] ; (*CountTofinish)--
406 mov r10, [rsp + 48] ; Pm16CodeSegment
407 mov rax, [rsp + 56] ; SevEsAPJumpTable
408 mov rbx, [rsp + 64] ; WakeupBuffer
409 mov rsp, r9 ; TopOfApStack
411 push rax ; Save SevEsAPJumpTable
412 push rbx ; Save WakeupBuffer
413 push r10 ; Save Pm16CodeSegment
414 push rcx ; Save MwaitSupport
415 push rdx ; Save ApTargetCState
417 lea rax, [PmEntryAmd] ; rax <- The start address of transition code
423 ; Clear R8 - R15, for reset, before going into 32-bit mode
435 ; Far return into 32-bit mode
442 btr eax, 31 ; Clear CR0.PG
443 mov cr0, eax ; Disable paging and caches
447 and ah, ~ 1 ; Clear LME
450 and al, ~ (1 << 5) ; Clear PAE
459 cmp cl, 1 ; Check mwait-monitor support
461 mov ebx, edx ; Save C-State to ebx
464 mov eax, esp ; Set Monitor Address
465 xor ecx, ecx ; ecx = 0
466 xor edx, edx ; edx = 0
468 mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]
474 pop edx ; PM16CodeSegment
476 pop ebx ; WakeupBuffer
478 pop eax ; SevEsAPJumpTable
480 cmp eax, 0 ; Check for SEV-ES
485 ; SEV-ES is enabled, use VMGEXIT (GHCB information already
493 ; Back from VMGEXIT AP_HLT_LOOP
494 ; Push the FLAGS/CS/IP values to use
496 push word 0x0002 ; EFLAGS
498 mov cx, [eax + 2] ; CS
502 push word 0x0000 ; For alignment, will be discarded
507 mov edx, esi ; Restore RDX reset value
517 AsmRelocateApLoopEndAmd: