From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.120]) by mx.groups.io with SMTP id smtpd.web12.22216.1574334131326003080 for ; Thu, 21 Nov 2019 03:02:11 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=MbCFoIJS; spf=pass (domain: redhat.com, ip: 207.211.31.120, mailfrom: lersek@redhat.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1574334130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sUPe2AkYb2QEkXMhhkA5Fz9KV0c+40xsrnOYj2QQeL0=; b=MbCFoIJS5d1Q6mz9t4LVmBk632QmAfUhk4jP7y0f77CLdGOJoAH2SF6OrvNl1Y/i1/F8WY P2HneGGsWe+g6+OttoyT/YQtRhbOsSX9ciElv9hi0XZhxUEzQL4d720LsMyDub1qoaakQ7 Z+TOdGQFuiWimAYjjkXoQXxQ/VT3MDU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-269--BfAG3UPPAKg_S-HCLHg1A-1; Thu, 21 Nov 2019 06:02:08 -0500 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 80219593A7; Thu, 21 Nov 2019 11:02:06 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-197.ams2.redhat.com [10.36.116.197]) by smtp.corp.redhat.com (Postfix) with ESMTP id F1C9D76FE7; Thu, 21 Nov 2019 11:02:03 +0000 (UTC) Subject: Re: [edk2-devel] [RFC PATCH v3 25/43] OvmfPkg/ResetVector: Add support for a 32-bit SEV check To: devel@edk2.groups.io, thomas.lendacky@amd.com Cc: Jordan Justen , Ard Biesheuvel , Michael D Kinney , Liming Gao , Eric Dong , Ray Ni , Brijesh Singh References: <18f6019180722fabdd7026ddaef6c643ff97f240.1574280425.git.thomas.lendacky@amd.com> From: "Laszlo Ersek" Message-ID: <48ea728f-403d-e415-9ddf-9231d0290ec5@redhat.com> Date: Thu, 21 Nov 2019 12:02:03 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 In-Reply-To: <18f6019180722fabdd7026ddaef6c643ff97f240.1574280425.git.thomas.lendacky@amd.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-MC-Unique: -BfAG3UPPAKg_S-HCLHg1A-1 X-Mimecast-Spam-Score: 0 Content-Language: en-US Content-Type: text/plain; charset=WINDOWS-1252 Content-Transfer-Encoding: quoted-printable On 11/20/19 21:06, Lendacky, Thomas wrote: > BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3D2198 >=20 > 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. >=20 > 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. >=20 > The exception handling support is active during the SEV check and uses th= e > OVMF temporary RAM space for a stack. After the SEV check is complete, th= e > exception handling support is removed and the stack pointer cleared. >=20 > Cc: Jordan Justen > Cc: Laszlo Ersek > Cc: Ard Biesheuvel > Signed-off-by: Tom Lendacky > --- > OvmfPkg/ResetVector/ResetVector.inf | 2 + > OvmfPkg/ResetVector/Ia32/PageTables64.asm | 261 +++++++++++++++++++++- > OvmfPkg/ResetVector/ResetVector.nasmb | 2 + > 3 files changed, 262 insertions(+), 3 deletions(-) >=20 > diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/Re= setVector.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/ResetVec= tor/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) > =20 > +; > +; 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 > =20 > @@ -48,8 +87,8 @@ CheckSevFeature: > jl NoSev > =20 > ; 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 > =20 > SevExit: > + ; > + ; Clear exception handlers and stack > + ; > + push eax > + mov eax, ADDR_OF(IdtrClear) > + lidt [cs:eax] > + pop eax > + mov esp, 0 > + > OneTimeCallRet CheckSevFeature > =20 > ; > @@ -146,3 +194,210 @@ pageTableEntriesLoop: > mov cr3, eax > =20 > 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] =3D> 0x100 - request termination > + ; EAX[15:12] =3D> 0x1 - OVMF > + ; EAX[23:16] =3D> 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 =3D> EAX, 1 =3D> EBX, 2 =3D> ECX, 3 =3D> EDX > + ; > + ; Set EAX as the first register to request. This will also be used a= s 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] =3D CPUID function > + ; GHCB_MSR[31:30] =3D CPUID register > + ; GHCB_MSR[11:0] =3D 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] =3D CPUID register value > + ; GHCB_MSR[31:30] =3D CPUID register > + ; GHCB_MSR[11:0] =3D 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 instructio= n > + ; (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 valu= e > +; 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_G= ATE_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_G= ATE_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_G= ATE_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 @@ > =20 > %define PT_ADDR(Offset) (FixedPcdGet32 (PcdOvmfSecPageTablesBase) + (O= ffset)) > %include "Ia32/Flat32ToFlat64.asm" > + > + %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBas= e) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) > %include "Ia32/PageTables64.asm" > %endif > =20 >=20 Reviewed-by: Laszlo Ersek