aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c')
-rw-r--r--roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
new file mode 100644
index 000000000..0182c9235
--- /dev/null
+++ b/roms/edk2/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
@@ -0,0 +1,453 @@
+/** @file
+
+ Stateful and implicitly initialized fw_cfg library implementation.
+
+ Copyright (C) 2013, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
+STATIC BOOLEAN mQemuFwCfgDmaSupported;
+
+STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol;
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return InternalQemuFwCfgIsAvailable ();
+}
+
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgInitialize (
+ VOID
+ )
+{
+ 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;
+ mQemuFwCfgDmaSupported = FALSE;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+ Signature = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ Revision = QemuFwCfgRead32 ();
+ DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+ if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
+ (Revision < 1)
+ ) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+ mQemuFwCfgSupported = FALSE;
+ return RETURN_SUCCESS;
+ }
+
+ if ((Revision & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
+ } else {
+ 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.
+
+ This function never changes fw_cfg state.
+
+ @retval TRUE The interface is available internally.
+ @retval FALSE The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return mQemuFwCfgSupported;
+}
+
+/**
+ Returns a boolean indicating whether QEMU provides the DMA-like access method
+ for fw_cfg.
+
+ @retval TRUE The DMA-like access method is available.
+ @retval FALSE The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+ VOID
+ )
+{
+ 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);
+ }
+}