On 11/20/19 21:06, Lendacky, Thomas wrote: > BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2198 > > During BSP startup, the reset vector code will issue a CPUID instruction > while in 32-bit mode. When running as an SEV-ES guest, this will trigger > a #VC exception. > > Add exception handling support to the early reset vector code to catch > these exceptions. Also, since the guest is in 32-bit mode at this point, > writes to the GHCB will be encrypted and thus not able to be read by the > hypervisor, so use the GHCB CPUID request/response protocol to obtain the > requested CPUID function values and provide these to the guest. > > The exception handling support is active during the SEV check and uses the > OVMF temporary RAM space for a stack. After the SEV check is complete, the > exception handling support is removed and the stack pointer cleared. > > Cc: Jordan Justen <jordan.l.jus...@intel.com> > Cc: Laszlo Ersek <ler...@redhat.com> > Cc: Ard Biesheuvel <ard.biesheu...@linaro.org> > Signed-off-by: Tom Lendacky <thomas.lenda...@amd.com> > --- > OvmfPkg/ResetVector/ResetVector.inf | 2 + > OvmfPkg/ResetVector/Ia32/PageTables64.asm | 261 +++++++++++++++++++++- > OvmfPkg/ResetVector/ResetVector.nasmb | 2 + > 3 files changed, 262 insertions(+), 3 deletions(-) > > diff --git a/OvmfPkg/ResetVector/ResetVector.inf > b/OvmfPkg/ResetVector/ResetVector.inf > index b0ddfa5832a2..960b47cd0797 100644 > --- a/OvmfPkg/ResetVector/ResetVector.inf > +++ b/OvmfPkg/ResetVector/ResetVector.inf > @@ -35,3 +35,5 @@ [BuildOptions] > [Pcd] > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize > + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase > + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize > diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm > b/OvmfPkg/ResetVector/Ia32/PageTables64.asm > index abad009f20f5..c902fa68d556 100644 > --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm > +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm > @@ -31,13 +31,52 @@ BITS 32 > PAGE_READ_WRITE + \ > PAGE_PRESENT) > > +; > +; SEV-ES #VC exception handler support > +; > +; #VC handler local variable locations > +; > +%define VC_CPUID_RESULT_EAX 0 > +%define VC_CPUID_RESULT_EBX 4 > +%define VC_CPUID_RESULT_ECX 8 > +%define VC_CPUID_RESULT_EDX 12 > +%define VC_GHCB_MSR_EDX 16 > +%define VC_GHCB_MSR_EAX 20 > +%define VC_CPUID_REQUEST_REGISTER 24 > +%define VC_CPUID_FUNCTION 28 > + > +; #VC handler total local variable size > +; > +%define VC_VARIABLE_SIZE 32 > + > +; #VC handler GHCB CPUID request/response protocol values > +; > +%define GHCB_CPUID_REQUEST 4 > +%define GHCB_CPUID_RESPONSE 5 > +%define GHCB_CPUID_REGISTER_SHIFT 30 > +%define CPUID_INSN_LEN 2 > + > + > ; Check if Secure Encrypted Virtualization (SEV) feature is enabled > ; > -; If SEV is enabled then EAX will be at least 32 > +; Modified: EAX, EBX, ECX, EDX, ESP > +; > +; If SEV is enabled then EAX will be at least 32. > ; If SEV is disabled then EAX will be zero. > ; > CheckSevFeature: > + ; > + ; Set up exception handlers to check for SEV-ES > + ; Load temporary RAM stack based on PCDs (see SevEsIdtVmmComm for > + ; stack usage) > + ; Establish exception handlers > + ; > + mov esp, SEV_ES_VC_TOP_OF_STACK > + mov eax, ADDR_OF(Idtr) > + lidt [cs:eax] > + > ; Check if we have a valid (0x8000_001F) CPUID leaf > + ; CPUID raises a #VC exception if running as an SEV-ES guest > mov eax, 0x80000000 > cpuid > > @@ -48,8 +87,8 @@ CheckSevFeature: > jl NoSev > > ; Check for memory encryption feature: > - ; CPUID Fn8000_001F[EAX] - Bit 1 > - ; > + ; CPUID Fn8000_001F[EAX] - Bit 1 > + ; CPUID raises a #VC exception if running as an SEV-ES guest > mov eax, 0x8000001f > cpuid > bt eax, 1 > @@ -73,6 +112,15 @@ NoSev: > xor eax, eax > > SevExit: > + ; > + ; Clear exception handlers and stack > + ; > + push eax > + mov eax, ADDR_OF(IdtrClear) > + lidt [cs:eax] > + pop eax > + mov esp, 0 > + > OneTimeCallRet CheckSevFeature > > ; > @@ -146,3 +194,210 @@ pageTableEntriesLoop: > mov cr3, eax > > OneTimeCallRet SetCr3ForPageTables64 > + > +SevEsIdtNotCpuid: > + ; > + ; Use VMGEXIT to request termination. > + ; 1 - #VC was not for CPUID > + ; > + mov eax, 1 > + jmp SevEsIdtTerminate > + > +SevEsIdtNoCpuidResponse: > + ; > + ; Use VMGEXIT to request termination. > + ; 2 - GHCB_CPUID_RESPONSE not received > + ; > + mov eax, 2 > + > +SevEsIdtTerminate: > + ; > + ; Use VMGEXIT to request termination. At this point the reason code is > + ; located in EAX, so shift it left 16 bits to the proper location. > + ; > + ; EAX[11:0] => 0x100 - request termination > + ; EAX[15:12] => 0x1 - OVMF > + ; EAX[23:16] => 0xXX - REASON CODE > + ; > + shl eax, 16 > + or eax, 0x1100 > + xor edx, edx > + mov ecx, 0xc0010130 > + wrmsr > + ; > + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit > + ; mode, so work around this by temporarily switching to 64-bit mode. > + ; > +BITS 64 > + rep vmmcall > +BITS 32 > + > + ; > + ; We shouldn't come back from the VMGEXIT, but if we do, just loop. > + ; > +SevEsIdtHlt: > + hlt > + jmp SevEsIdtHlt > + iret > + > + ; > + ; Total stack usage for the #VC handler is 44 bytes: > + ; - 12 bytes for the exception IRET (after popping error code) > + ; - 32 bytes for the local variables. > + ; > +SevEsIdtVmmComm: > + ; > + ; If we're here, then we are an SEV-ES guest and this > + ; was triggered by a CPUID instruction > + ; > + pop ecx ; Error code > + cmp ecx, 0x72 ; Be sure it was CPUID > + jne SevEsIdtNotCpuid > + > + ; Set up local variable room on the stack > + ; CPUID function : + 28 > + ; CPUID request register : + 24 > + ; GHCB MSR (EAX) : + 20 > + ; GHCB MSR (EDX) : + 16 > + ; CPUID result (EDX) : + 12 > + ; CPUID result (ECX) : + 8 > + ; CPUID result (EBX) : + 4 > + ; CPUID result (EAX) : + 0 > + sub esp, VC_VARIABLE_SIZE > + > + ; Save the CPUID function being requested > + mov [esp + VC_CPUID_FUNCTION], eax > + > + ; The GHCB CPUID protocol uses the following mapping to request > + ; a specific register: > + ; 0 => EAX, 1 => EBX, 2 => ECX, 3 => EDX > + ; > + ; Set EAX as the first register to request. This will also be used as a > + ; loop variable to request all register values (EAX to EDX). > + xor eax, eax > + mov [esp + VC_CPUID_REQUEST_REGISTER], eax > + > + ; Save current GHCB MSR value > + mov ecx, 0xc0010130 > + rdmsr > + mov [esp + VC_GHCB_MSR_EAX], eax > + mov [esp + VC_GHCB_MSR_EDX], edx > + > +NextReg: > + ; > + ; Setup GHCB MSR > + ; GHCB_MSR[63:32] = CPUID function > + ; GHCB_MSR[31:30] = CPUID register > + ; GHCB_MSR[11:0] = CPUID request protocol > + ; > + mov eax, [esp + VC_CPUID_REQUEST_REGISTER] > + cmp eax, 4 > + jge VmmDone > + > + shl eax, GHCB_CPUID_REGISTER_SHIFT > + or eax, GHCB_CPUID_REQUEST > + mov edx, [esp + VC_CPUID_FUNCTION] > + mov ecx, 0xc0010130 > + wrmsr > + > + ; > + ; Issue VMGEXIT - NASM doesn't support the vmmcall instruction in 32-bit > + ; mode, so work around this by temporarily switching to 64-bit mode. > + ; > +BITS 64 > + rep vmmcall > +BITS 32 > + > + ; > + ; Read GHCB MSR > + ; GHCB_MSR[63:32] = CPUID register value > + ; GHCB_MSR[31:30] = CPUID register > + ; GHCB_MSR[11:0] = CPUID response protocol > + ; > + mov ecx, 0xc0010130 > + rdmsr > + mov ecx, eax > + and ecx, 0xfff > + cmp ecx, GHCB_CPUID_RESPONSE > + jne SevEsIdtNoCpuidResponse > + > + ; Save returned value > + shr eax, GHCB_CPUID_REGISTER_SHIFT > + mov [esp + eax * 4], edx > + > + ; Next register > + inc word [esp + VC_CPUID_REQUEST_REGISTER] > + > + jmp NextReg > + > +VmmDone: > + ; > + ; At this point we have all CPUID register values. Restore the GHCB MSR, > + ; set the return register values and return. > + ; > + mov eax, [esp + VC_GHCB_MSR_EAX] > + mov edx, [esp + VC_GHCB_MSR_EDX] > + mov ecx, 0xc0010130 > + wrmsr > + > + mov eax, [esp + VC_CPUID_RESULT_EAX] > + mov ebx, [esp + VC_CPUID_RESULT_EBX] > + mov ecx, [esp + VC_CPUID_RESULT_ECX] > + mov edx, [esp + VC_CPUID_RESULT_EDX] > + > + add esp, VC_VARIABLE_SIZE > + > + ; Update the EIP value to skip over the now handled CPUID instruction > + ; (the CPUID instruction has a length of 2) > + add word [esp], CPUID_INSN_LEN > + iret > + > +ALIGN 2 > + > +Idtr: > + dw IDT_END - IDT_BASE - 1 ; Limit > + dd ADDR_OF(IDT_BASE) ; Base > + > +IdtrClear: > + dw 0 ; Limit > + dd 0 ; Base > + > +ALIGN 16 > + > +; > +; The Interrupt Descriptor Table (IDT) > +; This will be used to determine if SEV-ES is enabled. Upon execution > +; of the CPUID instruction, a VMM Communication Exception will occur. > +; This will tell us if SEV-ES is enabled. We can use the current value > +; of the GHCB MSR to determine the SEV attributes. > +; > +IDT_BASE: > +; > +; Vectors 0 - 28 (No handlers) > +; > +%rep 29 > + dw 0 ; Offset low bits 15..0 > + dw 0x10 ; Selector > + db 0 ; Reserved > + db 0x8E ; Gate Type > (IA32_IDT_GATE_TYPE_INTERRUPT_32) > + dw 0 ; Offset high bits 31..16 > +%endrep > +; > +; Vector 29 (VMM Communication Exception) > +; > + dw (ADDR_OF(SevEsIdtVmmComm) & 0xffff) ; Offset low bits 15..0 > + dw 0x10 ; Selector > + db 0 ; Reserved > + db 0x8E ; Gate Type > (IA32_IDT_GATE_TYPE_INTERRUPT_32) > + dw (ADDR_OF(SevEsIdtVmmComm) >> 16) ; Offset high bits 31..16 > +; > +; Vectors 30 - 31 (No handlers) > +; > +%rep 2 > + dw 0 ; Offset low bits 15..0 > + dw 0x10 ; Selector > + db 0 ; Reserved > + db 0x8E ; Gate Type > (IA32_IDT_GATE_TYPE_INTERRUPT_32) > + dw 0 ; Offset high bits 31..16 > +%endrep > +IDT_END: > diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb > b/OvmfPkg/ResetVector/ResetVector.nasmb > index 75cfe16654b1..579c75f5ba0c 100644 > --- a/OvmfPkg/ResetVector/ResetVector.nasmb > +++ b/OvmfPkg/ResetVector/ResetVector.nasmb > @@ -55,6 +55,8 @@ > > %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + > (Offset)) > %include "Ia32/Flat32ToFlat64.asm" > + > + %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + > FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) > %include "Ia32/PageTables64.asm" > %endif > >
Reviewed-by: Laszlo Ersek <ler...@redhat.com> -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#51046): https://edk2.groups.io/g/devel/message/51046 Mute This Topic: https://groups.io/mt/60973123/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-