From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-ot0-x243.google.com (mail-ot0-x243.google.com [IPv6:2607:f8b0:4003:c0f::243]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id DCFB6803FC for ; Tue, 21 Mar 2017 14:13:39 -0700 (PDT) Received: by mail-ot0-x243.google.com with SMTP id i1so23772470ota.3 for ; Tue, 21 Mar 2017 14:13:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:subject:to:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-transfer-encoding; bh=tIgw/P6bsU+YyZ6eaBqinBwlUfy5ZrVli/6wByl7zOo=; b=dGdGyyCFzBP6AXA19n61VCrBmtUmUTXSTvCVlemnwgB07s8M99so9oW+8/PTQpkIF0 WCpm5jr26SoHqN6Yi0i8hfX47+9xbkR5sbl5v3+nQMKsrdTUjg0jzATdlZBQiP03WBFi SoQ+hVJ5bFtuckurCScgYz2sdTK5W69AjXQT4Gmb80W62aLNj5xrqXOv7/Z7tjJgFHCB A/wQaP1wFFWq34tVPQnvtxPjxe0gfnN49jO1iU9WS3eBVfEBYlOxWWw1eXrJi4ggrnQZ /jbxRhp1bJh0Il7Z+t0fQTDtjU0VRZ+0whP27EfkqJ7/Aix+qsmRbVF0xpJd4Wu8bKAo uPQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:subject:to:cc:date:message-id:in-reply-to :references:user-agent:mime-version:content-transfer-encoding; bh=tIgw/P6bsU+YyZ6eaBqinBwlUfy5ZrVli/6wByl7zOo=; b=niKg4/L5pnzMVlM8Ivo20ZNMk83yt8z4uwxTxvS3DtGLXVo+eSxXc7pu3NoRtkv5zX aIGPD2oJtSWeqG1ayXnJRH5eF6SDC3vtl+dQs3j+x3/ZhosishC9Cyal3RQhlxFGZp9j ASYLRxbo2nW96kDzPbLbYxKcnynnuHK8Fh93Cx8jhGcm7YyEfFaTz2NfkoG4k3RAVR4r tpe8W4JAOTRqf53/4DNGFqsxjnO8pEllAuoLYtOZSkEufZNq3d7FKmigTrt1O9a51uDs MqvuRPTJ86QIAWUXix5KIeR+Wq3RGrTZMiWh+V5St6UC9hjYkc+EPYYMs/heEpxvGro6 Qe4A== X-Gm-Message-State: AFeK/H1Nwh/0bDSrXcaAdFxrG9j84Jxujbqy0iutzlmG9PBye8dEHJaDIxjApxHixa0PPw== X-Received: by 10.157.11.20 with SMTP id a20mr21954989ota.8.1490130819065; Tue, 21 Mar 2017 14:13:39 -0700 (PDT) Received: from [127.0.1.1] ([165.204.77.1]) by smtp.gmail.com with ESMTPSA id t69sm9158623ota.25.2017.03.21.14.13.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 21 Mar 2017 14:13:38 -0700 (PDT) From: Brijesh Singh X-Google-Original-From: Brijesh Singh To: michael.d.kinney@intel.com, jordan.l.justen@intel.com, edk2-devel@ml01.01.org, lersek@redhat.com, liming.gao@intel.com Cc: leo.duran@amd.com, brijesh.singh@amd.com, Thomas.Lendacky@amd.com Date: Tue, 21 Mar 2017 17:13:38 -0400 Message-ID: <149013081810.27235.18159090697032608822.stgit@brijesh-build-machine> In-Reply-To: <149013076154.27235.10725020825643505862.stgit@brijesh-build-machine> References: <149013076154.27235.10725020825643505862.stgit@brijesh-build-machine> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Subject: [RFC PATCH v2 09/10] OvmfPkg/QemuFwCfgLib: Add Secure Encrypted Virtualization (SEV) support X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Mar 2017 21:13:40 -0000 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit The patch adds SEV support in QemuFwCfgLib. When SEV is enabled: * Pei phase support IO-style operations. This is mainly because we need to use a bounce buffer inorder to support DMA operation. Allocate a memory for bounce buffer can get painful in Pei phase hence if we detect FWCfg DMA support then silently fallback to IO. * Dxe phase supports both IO and DMA style operations. --- OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c | 73 +++++++++++++ OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf | 2 OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c | 112 ++++++++++++++++++++ .../Library/QemuFwCfgLib/QemuFwCfgLibInternal.h | 38 +++++++ OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c | 93 +++++++++++++++++ OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf | 2 OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c | 82 +++++++++++++++ 7 files changed, 402 insertions(+) diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c index ac05f4c..be8e945 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c @@ -4,6 +4,7 @@ Copyright (C) 2013, Red Hat, Inc. Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this @@ -14,14 +15,34 @@ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include "Uefi.h" + +#include #include #include +#include +#include +#include #include "QemuFwCfgLibInternal.h" STATIC BOOLEAN mQemuFwCfgSupported = FALSE; STATIC BOOLEAN mQemuFwCfgDmaSupported; +STATIC BOOLEAN mQemuFwCfgSevIsEnabled = FALSE; + +/** + Returns a boolean indicating whether the SEV is enabled + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled +**/ +BOOLEAN +InternalQemuFwCfgSevIsEnabled ( + VOID + ) +{ + return mQemuFwCfgSevIsEnabled; +} /** Returns a boolean indicating if the firmware configuration interface @@ -79,6 +100,9 @@ QemuFwCfgInitialize ( mQemuFwCfgDmaSupported = TRUE; DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } + + mQemuFwCfgSevIsEnabled = MemEncryptSevIsEnabled (); + return RETURN_SUCCESS; } @@ -114,3 +138,52 @@ InternalQemuFwCfgDmaIsAvailable ( { return mQemuFwCfgDmaSupported; } + +/** + Allocate a bounce buffer for SEV DMA. + + @param[in] NumPage Number of pages. + @param[out] Buffer Allocated DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaAllocateBuffer ( + IN UINT32 NumPages, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + + // + // Allocate DMA bounce buffer + // + Status = BmDmaAllocateBuffer (TRUE, EfiBootServicesData, NumPages, Buffer); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "SEV: Failed to allocate bounce buffer %d pages\n", NumPages)); + ASSERT_EFI_ERROR (Status); + CpuDeadLoop (); + } + + DEBUG ((EFI_D_VERBOSE, "QemuFwCfgSevDma allocate buffer 0x%Lx Pages %d\n", (UINTN)Buffer, NumPages)); +} + +/** + Free the DMA buffer allocated using InternalQemuFwCfgSevDmaAllocateBuffer + + @param[in] NumPage Number of pages. + @param[in] Buffer DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaFreeBuffer ( + IN VOID *Buffer, + IN UINT32 NumPages + ) +{ + // + // Free the bounce buffer + // + DEBUG ((EFI_D_VERBOSE, "QemuFwCfgSevDma free buffer 0x%Lx Pages %d\n", (UINTN)Buffer, NumPages)); + BmDmaFreeBuffer (Buffer, NumPages); +} + diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf index 346bb88..536887f 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxeLib.inf @@ -47,4 +47,6 @@ DebugLib IoLib MemoryAllocationLib + MemEncryptSevLib + BmDmaLib diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c index 1bf725d..d2560a3 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c @@ -47,6 +47,111 @@ QemuFwCfgSelectItem ( /** + Transfer an array of bytes, or skip a number of bytes, using the SEV DMA bounce + interface. The function is same as InternalQemuFwCfgDmaBytes with excpetion that + it uses bounce buffer + + @param[in] Size Size in bytes to transfer or skip. + + @param[in,out] HostBuffer Buffer to read data into or write data from. Ignored, + and may be NULL, if Size is zero, or Control is + FW_CFG_DMA_CTL_SKIP. + + @param[in] Control One of the following: + FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer. + FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer. + FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg. +**/ +VOID +InternalQemuFwCfgSevDmaBytes ( + IN UINT32 Size, + IN OUT VOID *HostBuffer OPTIONAL, + IN UINT32 Control + ) +{ + volatile FW_CFG_DMA_ACCESS *Access; + UINT32 AccessHigh, AccessLow; + UINT32 Status; + UINT32 NumPages; + VOID *DmaBuffer, *Buffer; + + // + // Calculate number of pages we need to allocate for this operation + // + if (Control == FW_CFG_DMA_CTL_SKIP) { + // + // Control data does not need the actual buffer + // + NumPages = EFI_SIZE_TO_PAGES (sizeof (*Access)); + } else { + NumPages = EFI_SIZE_TO_PAGES (sizeof (*Access) + Size); + } + + // + // Allocate DMA bounce buffer + // + InternalQemuFwCfgSevDmaAllocateBuffer (NumPages, &DmaBuffer); + + Access = (FW_CFG_DMA_ACCESS *)DmaBuffer; + Buffer = DmaBuffer + sizeof(*Access); + + Access->Control = SwapBytes32 (Control); + Access->Length = SwapBytes32 (Size); + Access->Address = SwapBytes64 ((UINTN)Buffer); + + // + // Copy data from Host buffer into DMA buffer + // + if (HostBuffer && (Control == FW_CFG_DMA_CTL_WRITE)) { + CopyMem (Buffer, HostBuffer, Size); + } + + // + // Delimit the transfer from (a) modifications to Access, (b) in case of a + // write, from writes to Buffer by the caller. + // + MemoryFence (); + + // + // Start the transfer. + // + AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32); + AccessLow = (UINT32)(UINTN)Access; + IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh)); + IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow)); + + // + // Don't look at Access->Control before starting the transfer. + // + MemoryFence (); + + // + // Wait for the transfer to complete. + // + do { + Status = SwapBytes32 (Access->Control); + ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0); + } while (Status != 0); + + // + // After a read, the caller will want to use Buffer. + // + MemoryFence (); + + // + // Copy data from DMA buffer into Host Buffer + // + if (HostBuffer && (Control == FW_CFG_DMA_CTL_READ)) { + CopyMem (HostBuffer, Buffer, Size); + } + + // + // Free the DMA bounce buffer + // + InternalQemuFwCfgSevDmaFreeBuffer (DmaBuffer, NumPages); +} + +/** Transfer an array of bytes, or skip a number of bytes, using the DMA interface. @@ -79,6 +184,13 @@ InternalQemuFwCfgDmaBytes ( return; } + // + // When SEV is enabled then use SEV version of DmaReadWrite + // + if (InternalQemuFwCfgSevIsEnabled ()) { + return InternalQemuFwCfgSevDmaBytes (Size, Buffer, Control); + } + Access.Control = SwapBytes32 (Control); Access.Length = SwapBytes32 (Size); Access.Address = SwapBytes64 ((UINTN)Buffer); diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h index 6e87c62..8e2ff45 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h @@ -2,6 +2,7 @@ Internal interfaces specific to the QemuFwCfgLib instances in OvmfPkg. Copyright (C) 2016, Red Hat, Inc. + Copyright (C) 2017, Advanced Micro Devices. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this @@ -43,4 +44,41 @@ InternalQemuFwCfgDmaIsAvailable ( VOID ); +/** + Returns a boolean indicating whether the SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled +**/ +BOOLEAN +InternalQemuFwCfgSevIsEnabled ( + VOID + ); + +/** + Allocate a bounce buffer for SEV DMA. + + @param[in] NumPage Number of pages. + @param[out] Buffer Allocated DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaAllocateBuffer ( + IN UINT32 NumPages, + OUT VOID **Buffer + ); + +/** + Free the DMA buffer allocated using InternalQemuFwCfgSevDmaAllocateBuffer + + @param[in] NumPage Number of pages. + @param[in] Buffer DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaFreeBuffer ( + IN VOID *Buffer, + IN UINT32 NumPages + ); + #endif diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c index ac05f4c..3dc9270 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPei.c @@ -4,6 +4,7 @@ Copyright (C) 2013, Red Hat, Inc. Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this @@ -14,14 +15,55 @@ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include #include #include +#include +#include #include "QemuFwCfgLibInternal.h" STATIC BOOLEAN mQemuFwCfgSupported = FALSE; STATIC BOOLEAN mQemuFwCfgDmaSupported; +/** + Returns a boolean indicating whether the SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled +**/ +BOOLEAN +InternalQemuFwCfgSevIsEnabled ( + VOID + ) +{ + UINT32 RegEax; + MSR_SEV_STATUS_REGISTER Msr; + CPUID_MEMORY_ENCRYPTION_INFO_EAX Eax; + + // + // Check if memory encryption leaf exist + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_MEMORY_ENCRYPTION_INFO) { + // + // CPUID Fn8000_001F[EAX] Bit 1 (Sev supported) + // + AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, &Eax.Uint32, NULL, NULL, NULL); + + if (Eax.Bits.SevBit) { + // + // Check MSR_0xC0010131 Bit 0 (Sev Enabled) + // + Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS); + if (Msr.Bits.SevBit) { + return TRUE; + } + } + } + + return FALSE; +} /** Returns a boolean indicating if the firmware configuration interface @@ -79,6 +121,17 @@ QemuFwCfgInitialize ( mQemuFwCfgDmaSupported = TRUE; DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } + + // + // When SEV is enabled then we do not support DMA interface. + // This is because we need to use bounce buffer to support DMA operation in SEV guest. + // Allocating memory for bounce buffer can get painful in Pei phase + // + if (mQemuFwCfgDmaSupported && InternalQemuFwCfgSevIsEnabled ()) { + mQemuFwCfgDmaSupported = FALSE; + DEBUG ((DEBUG_INFO, "QemuFwCfg disabling DMA interface and defaulting to IO Port.\n")); + } + return RETURN_SUCCESS; } @@ -114,3 +167,43 @@ InternalQemuFwCfgDmaIsAvailable ( { return mQemuFwCfgDmaSupported; } + +/** + Allocate a bounce buffer for SEV DMA. + + @param[in] NumPage Number of pages. + @param[out] Buffer Allocated DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaAllocateBuffer ( + IN UINT32 NumPages, + OUT VOID **Buffer + ) +{ + // + // We should never reach here + // + ASSERT (FALSE); + CpuDeadLoop (); +} + +/** + Free the DMA buffer allocated using InternalQemuFwCfgSevDmaAllocateBuffer + + @param[in] NumPage Number of pages. + @param[in] Buffer DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaFreeBuffer ( + IN VOID *Buffer, + IN UINT32 NumPages + ) +{ + // + // We should never reach here + // + ASSERT (FALSE); + CpuDeadLoop (); +} diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf index 4f966a8..83cc0de 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiLib.inf @@ -39,7 +39,9 @@ [Packages] MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec [LibraryClasses] BaseLib diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c index 465ccbe..70b0a47 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c @@ -16,8 +16,11 @@ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ +#include #include #include +#include +#include #include "QemuFwCfgLibInternal.h" @@ -94,3 +97,82 @@ InternalQemuFwCfgDmaIsAvailable ( { return FALSE; } + +/** + Returns a boolean indicating whether the SEV is enabled + + @retval TRUE SEV is enabled + @retval FALSE SEV is not enabled +**/ +BOOLEAN +InternalQemuFwCfgSevIsEnabled ( + VOID + ) +{ + UINT32 RegEax; + MSR_SEV_STATUS_REGISTER Msr; + CPUID_MEMORY_ENCRYPTION_INFO_EAX Eax; + + // + // Check if memory encryption leaf exist + // + AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); + if (RegEax >= CPUID_MEMORY_ENCRYPTION_INFO) { + // + // CPUID Fn8000_001F[EAX] Bit 1 (Sev supported) + // + AsmCpuid (CPUID_MEMORY_ENCRYPTION_INFO, &Eax.Uint32, NULL, NULL, NULL); + + if (Eax.Bits.SevBit) { + // + // Check MSR_0xC0010131 Bit 0 (Sev Enabled) + // + Msr.Uint32 = AsmReadMsr32 (MSR_SEV_STATUS); + if (Msr.Bits.SevBit) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + Allocate a bounce buffer for SEV DMA. + + @param[in] NumPage Number of pages. + @param[out] Buffer Allocated DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaAllocateBuffer ( + IN UINT32 NumPages, + OUT VOID **Buffer + ) +{ + // + // We should never reach here + // + ASSERT (FALSE); + CpuDeadLoop (); +} + +/** + Free the DMA buffer allocated using InternalQemuFwCfgSevDmaAllocateBuffer + + @param[in] NumPage Number of pages. + @param[in] Buffer DMA Buffer pointer + +**/ +VOID +InternalQemuFwCfgSevDmaFreeBuffer ( + IN VOID *Buffer, + IN UINT32 NumPages + ) +{ + // + // We should never reach here + // + ASSERT (FALSE); + CpuDeadLoop (); +}