X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FLibrary%2FQemuFwCfgLib%2FQemuFwCfgDxe.c;h=f5787cd24d380a5776539dc37fd60eaa027eb307;hb=ac0a286f4d747a4c6c603a7b225917293cbe1e9f;hp=ac05f4c347f38986ad474373dfa19bef3512b0ca;hpb=fee47a261c65235e263aa128d28e7da8f8f9f100;p=mirror_edk2.git
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
index ac05f4c347..f5787cd24d 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
@@ -4,24 +4,29 @@
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
- distribution. The full text of the license may be found at
- http://opensource.org/licenses/bsd-license.php
-
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
**/
+#include
+
+#include
+
+#include
+#include
+#include
#include
#include
+#include
+#include
#include "QemuFwCfgLibInternal.h"
-STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
-STATIC BOOLEAN mQemuFwCfgDmaSupported;
+STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
+STATIC BOOLEAN mQemuFwCfgDmaSupported;
+STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol;
/**
Returns a boolean indicating if the firmware configuration interface
@@ -42,33 +47,33 @@ QemuFwCfgIsAvailable (
return InternalQemuFwCfgIsAvailable ();
}
-
RETURN_STATUS
EFIAPI
QemuFwCfgInitialize (
VOID
)
{
- UINT32 Signature;
- UINT32 Revision;
+ UINT32 Signature;
+ UINT32 Revision;
//
// Enable the access routines while probing to see if it is supported.
// For probing we always use the IO Port (IoReadFifo8()) access method.
//
- mQemuFwCfgSupported = TRUE;
+ mQemuFwCfgSupported = TRUE;
mQemuFwCfgDmaSupported = FALSE;
QemuFwCfgSelectItem (QemuFwCfgItemSignature);
Signature = QemuFwCfgRead32 ();
- DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));
+ DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
Revision = QemuFwCfgRead32 ();
- DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));
+ DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
(Revision < 1)
- ) {
- DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));
+ )
+ {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
mQemuFwCfgSupported = FALSE;
return RETURN_SUCCESS;
}
@@ -79,10 +84,34 @@ QemuFwCfgInitialize (
mQemuFwCfgDmaSupported = TRUE;
DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
}
+
+ if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {
+ EFI_STATUS Status;
+
+ //
+ // IoMmuDxe driver must have installed the IOMMU protocol. If we are not
+ // able to locate the protocol then something must have gone wrong.
+ //
+ Status = gBS->LocateProtocol (
+ &gEdkiiIoMmuProtocolGuid,
+ NULL,
+ (VOID **)&mIoMmuProtocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",
+ gEfiCallerBaseName,
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+ }
+
return RETURN_SUCCESS;
}
-
/**
Returns a boolean indicating if the firmware configuration interface is
available for library-internal purposes.
@@ -114,3 +143,351 @@ InternalQemuFwCfgDmaIsAvailable (
{
return mQemuFwCfgDmaSupported;
}
+
+/**
+ Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used
+ between Host and device to exchange the information. The buffer must be free'd
+ using FreeFwCfgDmaAccessBuffer ().
+
+**/
+STATIC
+VOID
+AllocFwCfgDmaAccessBuffer (
+ OUT VOID **Access,
+ OUT VOID **MapInfo
+ )
+{
+ UINTN Size;
+ UINTN NumPages;
+ EFI_STATUS Status;
+ VOID *HostAddress;
+ EFI_PHYSICAL_ADDRESS DmaAddress;
+ VOID *Mapping;
+
+ Size = sizeof (FW_CFG_DMA_ACCESS);
+ NumPages = EFI_SIZE_TO_PAGES (Size);
+
+ //
+ // As per UEFI spec, in order to map a host address with
+ // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU
+ // AllocateBuffer()
+ //
+ Status = mIoMmuProtocol->AllocateBuffer (
+ mIoMmuProtocol,
+ AllocateAnyPages,
+ EfiBootServicesData,
+ NumPages,
+ &HostAddress,
+ EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n",
+ gEfiCallerBaseName,
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ //
+ // Avoid exposing stale data even temporarily: zero the area before mapping
+ // it.
+ //
+ ZeroMem (HostAddress, Size);
+
+ //
+ // Map the host buffer with BusMasterCommonBuffer64
+ //
+ Status = mIoMmuProtocol->Map (
+ mIoMmuProtocol,
+ EdkiiIoMmuOperationBusMasterCommonBuffer64,
+ HostAddress,
+ &Size,
+ &DmaAddress,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n",
+ gEfiCallerBaseName,
+ __FUNCTION__
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ if (Size < sizeof (FW_CFG_DMA_ACCESS)) {
+ mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)sizeof (FW_CFG_DMA_ACCESS),
+ (UINT64)Size
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ *Access = HostAddress;
+ *MapInfo = Mapping;
+}
+
+/**
+ Function is to used for freeing the Access buffer allocated using
+ AllocFwCfgDmaAccessBuffer()
+
+**/
+STATIC
+VOID
+FreeFwCfgDmaAccessBuffer (
+ IN VOID *Access,
+ IN VOID *Mapping
+ )
+{
+ UINTN NumPages;
+ EFI_STATUS Status;
+
+ NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS));
+
+ Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to UnMap() Mapping 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)(UINTN)Mapping
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to Free() 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)(UINTN)Access
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+}
+
+/**
+ Function is used for mapping host address to device address. The buffer must
+ be unmapped with UnmapDmaDataBuffer ().
+
+**/
+STATIC
+VOID
+MapFwCfgDmaDataBuffer (
+ IN BOOLEAN IsWrite,
+ IN VOID *HostAddress,
+ IN UINT32 Size,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **MapInfo
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumberOfBytes;
+ VOID *Mapping;
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;
+
+ NumberOfBytes = Size;
+ Status = mIoMmuProtocol->Map (
+ mIoMmuProtocol,
+ (IsWrite ?
+ EdkiiIoMmuOperationBusMasterRead64 :
+ EdkiiIoMmuOperationBusMasterWrite64),
+ HostAddress,
+ &NumberOfBytes,
+ &PhysicalAddress,
+ &Mapping
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)(UINTN)HostAddress,
+ (UINT64)Size
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ if (NumberOfBytes < Size) {
+ mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ Size,
+ (UINT64)NumberOfBytes
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ *DeviceAddress = PhysicalAddress;
+ *MapInfo = Mapping;
+}
+
+STATIC
+VOID
+UnmapFwCfgDmaDataBuffer (
+ IN VOID *Mapping
+ )
+{
+ EFI_STATUS Status;
+
+ Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a failed to UnMap() Mapping 0x%Lx\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)(UINTN)Mapping
+ ));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+}
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer 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
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ )
+{
+ volatile FW_CFG_DMA_ACCESS LocalAccess;
+ volatile FW_CFG_DMA_ACCESS *Access;
+ UINT32 AccessHigh, AccessLow;
+ UINT32 Status;
+ VOID *AccessMapping, *DataMapping;
+ VOID *DataBuffer;
+
+ ASSERT (
+ Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
+ Control == FW_CFG_DMA_CTL_SKIP
+ );
+
+ if (Size == 0) {
+ return;
+ }
+
+ Access = &LocalAccess;
+ AccessMapping = NULL;
+ DataMapping = NULL;
+ DataBuffer = Buffer;
+
+ //
+ // When SEV is enabled, map Buffer to DMA address before issuing the DMA
+ // request
+ //
+ if (MemEncryptSevIsEnabled ()) {
+ VOID *AccessBuffer;
+ EFI_PHYSICAL_ADDRESS DataBufferAddress;
+
+ //
+ // Allocate DMA Access buffer
+ //
+ AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping);
+
+ Access = AccessBuffer;
+
+ //
+ // Map actual data buffer
+ //
+ if (Control != FW_CFG_DMA_CTL_SKIP) {
+ MapFwCfgDmaDataBuffer (
+ Control == FW_CFG_DMA_CTL_WRITE,
+ Buffer,
+ Size,
+ &DataBufferAddress,
+ &DataMapping
+ );
+
+ DataBuffer = (VOID *)(UINTN)DataBufferAddress;
+ }
+ }
+
+ Access->Control = SwapBytes32 (Control);
+ Access->Length = SwapBytes32 (Size);
+ Access->Address = SwapBytes64 ((UINTN)DataBuffer);
+
+ //
+ // 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 ();
+
+ //
+ // If Access buffer was dynamically allocated then free it.
+ //
+ if (AccessMapping != NULL) {
+ FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping);
+ }
+
+ //
+ // If DataBuffer was mapped then unmap it.
+ //
+ if (DataMapping != NULL) {
+ UnmapFwCfgDmaDataBuffer (DataMapping);
+ }
+}