[edk2-devel] [PATCH V3 06/10] OvmfPkg: Add AmdSev.asm in ResetVector

Min Xu min.m.xu at intel.com
Tue Jul 27 05:42:23 UTC 2021


RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429

AmdSev.asm includes below routines:
 - CheckSevFeatures
   Check if Secure Encrypted Virtualization (SEV) features are enabled.
 - PreSetCr3ForPageTables64Sev
   It is called before SetCr3ForPageTables64 in SEV guests.
 - PostSetCr3PageTables64Sev
   It is called after SetCr3PageTables64 in SEV guests.
 - PostJump64BitAndLandHereSev
   It is called after Jump64BitAndLandHere in SEV guests.
 - #VC exception handling routines

These routines are extracted from PageTables64.asm and Flat32ToFlat64.asm
Need AMD engineers' help to review/validate the patch so that there is
no regression. Thanks in advance!

Note:
  In above Pre/Post routines, dword[TDX_WORK_AREA] should be checked
  to see if it is 'TDXG' (Tdx guests). This is because some memory region
  for example, byte[SEV_ES_WORK_AREA] cannot be accessed in Tdx guests.
  Tdx requires that any memory region to be accessed should be accepted
  first or initialized by host VMM before Td guest is launched.

Cc: Ard Biesheuvel <ardb+tianocore at kernel.org>
Cc: Jordan Justen <jordan.l.justen at intel.com>
Cc: Brijesh Singh <brijesh.singh at amd.com>
Cc: Erdem Aktas <erdemaktas at google.com>
Cc: James Bottomley <jejb at linux.ibm.com>
Cc: Jiewen Yao <jiewen.yao at intel.com>
Cc: Tom Lendacky <thomas.lendacky at amd.com>
Signed-off-by: Min Xu <min.m.xu at intel.com>
---
 OvmfPkg/ResetVector/Ia32/AmdSev.asm | 526 ++++++++++++++++++++++++++++
 1 file changed, 526 insertions(+)
 create mode 100644 OvmfPkg/ResetVector/Ia32/AmdSev.asm

diff --git a/OvmfPkg/ResetVector/Ia32/AmdSev.asm b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
new file mode 100644
index 000000000000..962b7e169c61
--- /dev/null
+++ b/OvmfPkg/ResetVector/Ia32/AmdSev.asm
@@ -0,0 +1,526 @@
+;------------------------------------------------------------------------------
+; @file
+; AMD SEV routines
+;
+; Copyright (c) 2020, Advanced Micro Devices, Inc. All rights reserved.<BR>
+; Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+;
+; SPDX-License-Identifier: BSD-2-Clause-Patent
+;
+;------------------------------------------------------------------------------
+
+
+BITS  32
+;
+; 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) features are enabled.
+;
+; Register usage is tight in this routine, so multiple calls for the
+; same CPUID and MSR data are performed to keep things simple.
+;
+; 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.
+;
+CheckSevFeatures:
+    ; Set the first byte of the workarea to zero to communicate to the SEC
+    ; phase that SEV-ES is not enabled. If SEV-ES is enabled, the CPUID
+    ; instruction will trigger a #VC exception where the first byte of the
+    ; workarea will be set to one or, if CPUID is not being intercepted,
+    ; the MSR check below will set the first byte of the workarea to one.
+    mov     byte[SEV_ES_WORK_AREA], 0
+
+    ;
+    ; 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
+
+    ; This check should fail on Intel or Non SEV AMD CPUs. In future if
+    ; Intel CPUs supports this CPUID leaf then we are guranteed to have exact
+    ; same bit definition.
+    cmp       eax, 0x8000001f
+    jl        NoSev
+
+    ; Check for SEV memory encryption feature:
+    ; 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
+    jnc       NoSev
+
+    ; Check if SEV memory encryption is enabled
+    ;  MSR_0xC0010131 - Bit 0 (SEV enabled)
+    mov       ecx, 0xc0010131
+    rdmsr
+    bt        eax, 0
+    jnc       NoSev
+
+    ; Check for SEV-ES memory encryption feature:
+    ; CPUID  Fn8000_001F[EAX] - Bit 3
+    ;   CPUID raises a #VC exception if running as an SEV-ES guest
+    mov       eax, 0x8000001f
+    cpuid
+    bt        eax, 3
+    jnc       GetSevEncBit
+
+    ; Check if SEV-ES is enabled
+    ;  MSR_0xC0010131 - Bit 1 (SEV-ES enabled)
+    mov       ecx, 0xc0010131
+    rdmsr
+    bt        eax, 1
+    jnc       GetSevEncBit
+
+    ; Set the first byte of the workarea to one to communicate to the SEC
+    ; phase that SEV-ES is enabled.
+    mov       byte[SEV_ES_WORK_AREA], 1
+
+GetSevEncBit:
+    ; Get pte bit position to enable memory encryption
+    ; CPUID Fn8000_001F[EBX] - Bits 5:0
+    ;
+    and       ebx, 0x3f
+    mov       eax, ebx
+
+    ; The encryption bit position is always above 31
+    sub       ebx, 32
+    jns       SevSaveMask
+
+    ; Encryption bit was reported as 31 or below, enter a HLT loop
+SevEncBitLowHlt:
+    cli
+    hlt
+    jmp       SevEncBitLowHlt
+
+SevSaveMask:
+    xor       edx, edx
+    bts       edx, ebx
+
+    mov       dword[SEV_ES_WORK_AREA_ENC_MASK], 0
+    mov       dword[SEV_ES_WORK_AREA_ENC_MASK + 4], edx
+    jmp       SevExit
+
+NoSev:
+    ;
+    ; Perform an SEV-ES sanity check by seeing if a #VC exception occurred.
+    ;
+    cmp       byte[SEV_ES_WORK_AREA], 0
+    jz        NoSevPass
+
+    ;
+    ; A #VC was received, yet CPUID indicates no SEV-ES support, something
+    ; isn't right.
+    ;
+NoSevEsVcHlt:
+    cli
+    hlt
+    jmp       NoSevEsVcHlt
+
+NoSevPass:
+    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 CheckSevFeatures
+
+;
+; Called before SetCr3ForPageTables64 in SEV guests
+;
+; Modified: EAX, EBX, ECX, EDX, ESP
+;
+PreSetCr3ForPageTables64Sev:
+    ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
+    ; CheckSevFeatures cannot be called in Tdx guest because SEV_ES_WORK_AREA
+    ; cannot be accessed in this situation. Any memory region to be accessed
+    ; in Td guest should be accepted first.
+    cmp     dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+    jz      ExitPreSetCr3ForPageTables64Sev
+
+    OneTimeCall     CheckSevFeatures
+
+ExitPreSetCr3ForPageTables64Sev:
+    OneTimeCallRet  PreSetCr3ForPageTables64Sev
+
+;
+; It is called in SEV after SetCr3PageTables64
+;
+; Modified:  EAX, EBX, ECX, EDX, ESP
+;
+PostSetCr3PageTables64Sev:
+    ; In Tdx TDX_WORK_AREA was set to 'TDXG'.
+    ; CheckSevFeatures cannot be called in Tdx because SEV_ES_WORK_AREA
+    ; cannot be accessed in this situation. Any memory region to be accessed
+    ; in Td guest should be accepted first.
+    cmp     dword[TDX_WORK_AREA], 0x47584454 ; 'TDXG'
+    jz      ExitPostSetCr3PageTables64Sev
+
+    mov     eax, cr4
+    bts     eax, 5                      ; enable PAE
+    mov     cr4, eax
+
+    mov     ecx, 0xc0000080
+    rdmsr
+    bts     eax, 8                      ; set LME
+    wrmsr
+
+    ;
+    ; SEV-ES mitigation check support
+    ;
+    xor     ebx, ebx
+
+    cmp     byte[SEV_ES_WORK_AREA], 0
+    jz      EnablePaging
+
+    ;
+    ; SEV-ES is active, perform a quick sanity check against the reported
+    ; encryption bit position. This is to help mitigate against attacks where
+    ; the hypervisor reports an incorrect encryption bit position.
+    ;
+    ; This is the first step in a two step process. Before paging is enabled
+    ; writes to memory are encrypted. Using the RDRAND instruction (available
+    ; on all SEV capable processors), write 64-bits of random data to the
+    ; SEV_ES_WORK_AREA and maintain the random data in registers (register
+    ; state is protected under SEV-ES). This will be used in the second step.
+    ;
+RdRand1:
+    rdrand  ecx
+    jnc     RdRand1
+    mov     dword[SEV_ES_WORK_AREA_RDRAND], ecx
+RdRand2:
+    rdrand  edx
+    jnc     RdRand2
+    mov     dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
+
+    ;
+    ; Use EBX instead of the SEV_ES_WORK_AREA memory to determine whether to
+    ; perform the second step.
+    ;
+    mov     ebx, 1
+
+EnablePaging:
+    mov     eax, cr0
+    bts     eax, 31                     ; set PG
+    mov     cr0, eax                    ; enable paging
+
+ExitPostSetCr3PageTables64Sev:
+
+    OneTimeCallRet PostSetCr3PageTables64Sev
+
+;
+; Start of #VC exception handling routines
+;
+
+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
+    ;
+    ; Set the first byte of the workarea to one to communicate that
+    ; a #VC was taken.
+    mov     byte[SEV_ES_WORK_AREA], 1
+
+    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:
+
+BITS 64
+
+;
+; Called after Jump64BitAndLandHere
+;
+PostJump64BitAndLandHereSev:
+
+    ;
+    ; If it is Tdx guest, jump to exit point directly.
+    ; This is because following code may access the memory region which has
+    ; not been accepted. It is not allowed in Tdx guests.
+    ;
+    mov     eax, dword[TDX_WORK_AREA]
+    cmp     eax, 0x47584454             ; 'TDXG'
+    jz      GoodCompare
+
+    ;
+    ; Check if the second step of the SEV-ES mitigation is to be performed.
+    ;
+    test    ebx, ebx
+    jz      InsnCompare
+
+    ;
+    ; SEV-ES is active, perform the second step of the encryption bit postion
+    ; mitigation check. The ECX and EDX register contain data from RDRAND that
+    ; was stored to memory in encrypted form. If the encryption bit position is
+    ; valid, the contents of ECX and EDX will match the memory location.
+    ;
+    cmp     dword[SEV_ES_WORK_AREA_RDRAND], ecx
+    jne     SevEncBitHlt
+    cmp     dword[SEV_ES_WORK_AREA_RDRAND + 4], edx
+    jne     SevEncBitHlt
+
+    ;
+    ; If SEV or SEV-ES is active, perform a quick sanity check against
+    ; the reported encryption bit position. This is to help mitigate
+    ; against attacks where the hypervisor reports an incorrect encryption
+    ; bit position. If SEV is not active, this check will always succeed.
+    ;
+    ; The cmp instruction compares the first four bytes of the cmp instruction
+    ; itself (which will be read decrypted if SEV or SEV-ES is active and the
+    ; encryption bit position is valid) against the immediate within the
+    ; instruction (an instruction fetch is always decrypted correctly by
+    ; hardware) based on RIP relative addressing.
+    ;
+InsnCompare:
+    cmp     dword[rel InsnCompare], 0xFFF63D81
+    je      GoodCompare
+
+    ;
+    ; The hypervisor provided an incorrect encryption bit position, do not
+    ; proceed.
+    ;
+SevEncBitHlt:
+    cli
+    hlt
+    jmp     SevEncBitHlt
+
+GoodCompare:
+    OneTimeCallRet PostJump64BitAndLandHereSev
-- 
2.29.2.windows.2



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#78196): https://edk2.groups.io/g/devel/message/78196
Mute This Topic: https://groups.io/mt/84476064/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list