__FUNCTION__ is a pre-standard extension that gcc and Visual C++ among others support, while __func__ was standardized in C99. Since it's more standard, replace __FUNCTION__ with __func__ throughout OvmfPkg. Signed-off-by: Rebecca Cran <rebecca@bsdio.com> Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
		
			
				
	
	
		
			495 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			495 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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/MemEncryptTdxLib.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 () || (MemEncryptTdxIsEnabled ()))) {
 | |
|     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,
 | |
|         "QemuFwCfgDma %a:%a Failed to locate IOMMU protocol.\n",
 | |
|         gEfiCallerBaseName,
 | |
|         __func__
 | |
|         ));
 | |
|       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,
 | |
|       __func__
 | |
|       ));
 | |
|     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,
 | |
|       __func__
 | |
|       ));
 | |
|     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,
 | |
|       __func__,
 | |
|       (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,
 | |
|       __func__,
 | |
|       (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,
 | |
|       __func__,
 | |
|       (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,
 | |
|       __func__,
 | |
|       (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,
 | |
|       __func__,
 | |
|       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,
 | |
|       __func__,
 | |
|       (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 or TDX is enabled, map Buffer to DMA address before issuing the DMA
 | |
|   // request
 | |
|   //
 | |
|   if (MemEncryptSevIsEnabled () || MemEncryptTdxIsEnabled ()) {
 | |
|     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);
 | |
|   }
 | |
| }
 |