Generated mechanically with:
find OvmfPkg -type f -exec sed -i -e 's/EFI_D_/DEBUG_/g' {} \;
Signed-off-by: Rebecca Cran <rebecca@bsdio.com>
Cc: Philippe Mathieu-Daude <philmd@redhat.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ard Biesheuvel <ard.biesheuvel@arm.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Message-Id: <20200429215327.606467-1-rebecca@bsdio.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
	
		
			
				
	
	
		
			454 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			454 lines
		
	
	
		
			12 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/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);
 | 
						|
  }
 | 
						|
}
 |