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

Brijesh Singh via groups.io brijesh.singh=amd.com at groups.io
Tue Jul 27 10:56:35 UTC 2021


Hi Min,

This refactoring is already done by the SNP patch series.

https://edk2.groups.io/g/devel/message/77336?p=,,,20,0,0,0::Created,,posterid%3A5969970,20,2,20,83891510

It appears that you are also pulling in some of TDX logic inside the
AMDSev.asm such as

;
+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

Why we are referring the TDX workarea inside the AmdSev.asm ?

I will take out my refactoring patch outside of the SNP series and
submit it so that you can build on top of. This will simplify review
process.

thanks


On 7/27/21 12:42 AM, Min Xu wrote:
> 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


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#78208): https://edk2.groups.io/g/devel/message/78208
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