From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.81]) by mx.groups.io with SMTP id smtpd.web09.9240.1580979086215533761 for ; Thu, 06 Feb 2020 00:51:26 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=Ux8GGduK; spf=pass (domain: redhat.com, ip: 207.211.31.81, mailfrom: lersek@redhat.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1580979085; 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=8jCCKFVcs9Z2n3r8QZmQ0vY6aw3/HvXrw/7a8e3Y4wI=; b=Ux8GGduKkVYCu7h+nrHx5ekCgJDnvpWHfIkRb8HpZBri5HkDFC53VbjJzJbCdm1y2fJdBo 5Vpv5nA+guICmA4UsvEG98vgFeK3WipZ2ZkV7ncNSgeZuaVrLIZwC57K+iUhDTDSvBsTGb 18Sm61dSPdG0g31fEtBqhwx15xs36Uo= 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-149-IfuEYsJRO1a2PCj-kqnhaA-1; Thu, 06 Feb 2020 03:51:20 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D24711081FA3; Thu, 6 Feb 2020 08:51:18 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-117-20.ams2.redhat.com [10.36.117.20]) by smtp.corp.redhat.com (Postfix) with ESMTP id 73AB55C1B2; Thu, 6 Feb 2020 08:51:16 +0000 (UTC) Subject: Re: [edk2-devel] [PATCH v4 31/40] 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: <68967471a5e9c2652c1a43ff2d5557e5983c0625.1580857303.git.thomas.lendacky@amd.com> From: "Laszlo Ersek" Message-ID: <9c0cbb02-d9f1-4686-0fe8-37d8bbc10b77@redhat.com> Date: Thu, 6 Feb 2020 09:51:15 +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: <68967471a5e9c2652c1a43ff2d5557e5983c0625.1580857303.git.thomas.lendacky@amd.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-MC-Unique: IfuEYsJRO1a2PCj-kqnhaA-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit On 02/05/20 00:01, 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 > Cc: Laszlo Ersek > Cc: Ard Biesheuvel > Reviewed-by: Laszlo Ersek > Signed-off-by: Tom Lendacky > --- > OvmfPkg/ResetVector/ResetVector.inf | 3 + > OvmfPkg/ResetVector/Ia32/PageTables64.asm | 275 +++++++++++++++++++++- > OvmfPkg/ResetVector/ResetVector.nasmb | 2 + > 3 files changed, 277 insertions(+), 3 deletions(-) The new SEV_ES_WORK_AREA actions look OK to me. Thanks Laszlo > > diff --git a/OvmfPkg/ResetVector/ResetVector.inf b/OvmfPkg/ResetVector/ResetVector.inf > index 9aedbe9b3640..f9e9578d22b2 100644 > --- a/OvmfPkg/ResetVector/ResetVector.inf > +++ b/OvmfPkg/ResetVector/ResetVector.inf > @@ -36,7 +36,10 @@ [BuildOptions] > [Pcd] > gUefiCpuPkgTokenSpaceGuid.PcdSecGhcbBase > gUefiCpuPkgTokenSpaceGuid.PcdSecGhcbSize > + gUefiCpuPkgTokenSpaceGuid.PcdSevEsWorkAreaBase > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableBase > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecGhcbPageTableSize > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesBase > gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPageTablesSize > + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase > + gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize > diff --git a/OvmfPkg/ResetVector/Ia32/PageTables64.asm b/OvmfPkg/ResetVector/Ia32/PageTables64.asm > index 9f86ddf6f08f..7c72128a84d6 100644 > --- a/OvmfPkg/ResetVector/Ia32/PageTables64.asm > +++ b/OvmfPkg/ResetVector/Ia32/PageTables64.asm > @@ -36,13 +36,58 @@ 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 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. > + 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 > > @@ -53,8 +98,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 > @@ -78,6 +123,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 > > ; Check if Secure Encrypted Virtualization - Encrypted State (SEV-ES) feature > @@ -222,3 +276,218 @@ SetCr3: > mov cr3, eax > > OneTimeCallRet SetCr3ForPageTables64 > + > +; > +; 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 to the SEC > + ; phase that SEV-ES is enabled. > + 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: > diff --git a/OvmfPkg/ResetVector/ResetVector.nasmb b/OvmfPkg/ResetVector/ResetVector.nasmb > index c25932513b80..97e36ef591ab 100644 > --- a/OvmfPkg/ResetVector/ResetVector.nasmb > +++ b/OvmfPkg/ResetVector/ResetVector.nasmb > @@ -72,6 +72,8 @@ > %define GHCB_PT_ADDR (FixedPcdGet32 (PcdOvmfSecGhcbPageTableBase)) > %define GHCB_BASE (FixedPcdGet32 (PcdSecGhcbBase)) > %define GHCB_SIZE (FixedPcdGet32 (PcdSecGhcbSize)) > + %define SEV_ES_WORK_AREA (FixedPcdGet32 (PcdSevEsWorkAreaBase)) > + %define SEV_ES_VC_TOP_OF_STACK (FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) > %include "Ia32/PageTables64.asm" > %endif > >