[edk2-devel] [RFC PATCH 02/28] OvmfPkg/ResetVector: Add support for a 32-bit SEV check

Lendacky, Thomas posted 28 patches 5 years, 3 months ago
There is a newer version of this series
[edk2-devel] [RFC PATCH 02/28] OvmfPkg/ResetVector: Add support for a 32-bit SEV check
Posted by Lendacky, Thomas 5 years, 3 months ago
From: Tom Lendacky <thomas.lendacky@amd.com>

When running as an SEV-ES guest in 32-bit mode, it is not possible to
perform a CPUID instruction because it will require communicating with
the hypervisor using the GHCB.  However, writes to the GHCB when in
32-bit mode will be will be encrypted and thus not able to be read
by the hypervisor.

To get around this, add an IDT entry for the #VC exception.  The #VC
handler will use the GHCB CPUID request/response protocol to obtain
the requested CPUID function values and provide these to the guest.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 OvmfPkg/ResetVector/ResetVector.inf       |   2 +
 OvmfPkg/ResetVector/Ia32/PageTables64.asm | 173 ++++++++++++++++++++++
 OvmfPkg/ResetVector/ResetVector.nasmb     |   1 +
 3 files changed, 176 insertions(+)

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..c6071fe934de 100644
--- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm
+++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm
@@ -37,6 +37,15 @@ BITS    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
+    ;   Establish exception handlers
+    ;
+    mov       esp, SEV_TOP_OF_STACK
+    mov       eax, ADDR_OF(idtr)
+    lidt      [cs:eax]
+
     ; Check if we have a valid (0x8000_001F) CPUID leaf
     mov       eax, 0x80000000
     cpuid
@@ -73,6 +82,15 @@ NoSev:
     xor       eax, eax
 
 SevExit:
+    ;
+    ; Clear exception handlers and stack
+    ;
+    push      eax
+    mov       eax, ADDR_OF(idtr_clear)
+    lidt      [cs:eax]
+    pop       eax
+    mov       esp, 0
+
     OneTimeCallRet CheckSevFeature
 
 ;
@@ -146,3 +164,158 @@ pageTableEntriesLoop:
     mov     cr3, eax
 
     OneTimeCallRet SetCr3ForPageTables64
+
+SevEsIdtCommon:
+    hlt
+    jmp     SevEsIdtCommon
+    iret
+
+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     SevEsIdtCommon
+
+    ;
+    ; Set up local variable room on the stack
+    ;   CPUID function      : + 28
+    ;   CPUID 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, 32
+
+    ; Save CPUID function and initial register request
+    mov     [esp + 28], eax
+    xor     eax, eax
+    mov     [esp + 24], eax
+
+    ; Save current GHCB MSR value
+    mov     ecx, 0xc0010130
+    rdmsr
+    mov     [esp + 20], eax
+    mov     [esp + 16], 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 + 24]
+    cmp     eax, 4
+    jge     VmmDone
+
+    shl     eax, 30
+    or      eax, 0x004
+    mov     edx, [esp + 28]
+    mov     ecx, 0xc0010130
+    wrmsr
+
+    ; Issue VMGEXIT (rep; vmmcall)
+    db      0xf3
+    db      0x0f
+    db      0x01
+    db      0xd9
+
+    ;
+    ; 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, 0x005
+    jne     SevEsIdtCommon
+
+    ; Save returned value
+    shr     eax, 30
+    and     eax, 0x3
+    shl     eax, 2
+    mov     ecx, esp
+    add     ecx, eax
+    mov     [ecx], edx
+
+    ; Next register
+    inc     word [esp + 24]
+
+    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 + 20]
+    mov     edx, [esp + 16]
+    mov     ecx, 0xc0010130
+    wrmsr
+
+    mov     eax, [esp + 0]
+    mov     ebx, [esp + 4]
+    mov     ecx, [esp + 8]
+    mov     edx, [esp + 12]
+
+    add     esp, 32
+    add     word [esp], 2           ; Skip over the CPUID instruction
+    iret
+
+ALIGN   2
+
+idtr:
+    dw      IDT_END - IDT_BASE - 1  ; Limit
+    dd      ADDR_OF(IDT_BASE)       ; Base
+
+idtr_clear:
+    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
+;
+%rep 29
+    dw      (ADDR_OF(SevEsIdtCommon) & 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(SevEsIdtCommon) >> 16)      ; 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
+;
+%rep 2
+    dw      (ADDR_OF(SevEsIdtCommon) & 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(SevEsIdtCommon) >> 16)      ; Offset high bits 31..16
+%endrep
+IDT_END:
diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb
index 75cfe16654b1..3b213cd05ab2 100644
--- a/OvmfPkg/ResetVector/ResetVector.nasmb
+++ b/OvmfPkg/ResetVector/ResetVector.nasmb
@@ -55,6 +55,7 @@
 
   %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (Offset))
 %include "Ia32/Flat32ToFlat64.asm"
+  %define SEV_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
 %include "Ia32/PageTables64.asm"
 %endif
 
-- 
2.17.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#46110): https://edk2.groups.io/g/devel/message/46110
Mute This Topic: https://groups.io/mt/32966282/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-