Remove global variables, store the state in PlatformInfoHob instead. Probing for fw_cfg happens on first use, at library initialization time the Hob might not be present yet. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Tested-by: Tom Lendacky <thomas.lendacky@amd.com> Acked-by: Ard Biesheuvel <ardb@kernel.org>
		
			
				
	
	
		
			245 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			5.9 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 <PiPei.h>
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <Library/HobLib.h>
 | |
| #include <Library/IoLib.h>
 | |
| #include <Library/PlatformInitLib.h>
 | |
| #include <Library/QemuFwCfgLib.h>
 | |
| #include <WorkArea.h>
 | |
| 
 | |
| #include "QemuFwCfgLibInternal.h"
 | |
| 
 | |
| /**
 | |
|   Check if it is Tdx guest
 | |
| 
 | |
|   @retval    TRUE   It is Tdx guest
 | |
|   @retval    FALSE  It is not Tdx guest
 | |
| **/
 | |
| STATIC
 | |
| BOOLEAN
 | |
| QemuFwCfgIsCcGuest (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER  *CcWorkAreaHeader;
 | |
| 
 | |
|   CcWorkAreaHeader = (CONFIDENTIAL_COMPUTING_WORK_AREA_HEADER *)FixedPcdGet32 (PcdOvmfWorkAreaBase);
 | |
|   return (CcWorkAreaHeader != NULL && CcWorkAreaHeader->GuestType != CcGuestTypeNonEncrypted);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 ();
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| VOID
 | |
| QemuFwCfgProbe (
 | |
|   BOOLEAN  *Supported,
 | |
|   BOOLEAN  *DmaSupported
 | |
|   )
 | |
| {
 | |
|   UINT32   Signature;
 | |
|   UINT32   Revision;
 | |
|   BOOLEAN  CcGuest;
 | |
| 
 | |
|   // Use direct Io* calls for probing to avoid recursion.
 | |
|   IoWrite16 (FW_CFG_IO_SELECTOR, (UINT16)QemuFwCfgItemSignature);
 | |
|   IoReadFifo8 (FW_CFG_IO_DATA, sizeof Signature, &Signature);
 | |
|   IoWrite16 (FW_CFG_IO_SELECTOR, (UINT16)QemuFwCfgItemInterfaceVersion);
 | |
|   IoReadFifo8 (FW_CFG_IO_DATA, sizeof Revision, &Revision);
 | |
|   CcGuest = QemuFwCfgIsCcGuest ();
 | |
| 
 | |
|   *Supported    = FALSE;
 | |
|   *DmaSupported = FALSE;
 | |
|   if ((Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) && (Revision >= 1)) {
 | |
|     *Supported = TRUE;
 | |
|     if ((Revision & FW_CFG_F_DMA) && !CcGuest) {
 | |
|       *DmaSupported = TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DEBUG ((
 | |
|     DEBUG_INFO,
 | |
|     "%a: Supported %d, DMA %d\n",
 | |
|     __func__,
 | |
|     *Supported,
 | |
|     *DmaSupported
 | |
|     ));
 | |
| }
 | |
| 
 | |
| STATIC
 | |
| EFI_HOB_PLATFORM_INFO *
 | |
| QemuFwCfgGetPlatformInfo (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_HOB_PLATFORM_INFO  *PlatformInfoHob;
 | |
|   EFI_HOB_GUID_TYPE      *GuidHob;
 | |
| 
 | |
|   GuidHob = GetFirstGuidHob (&gUefiOvmfPkgPlatformInfoGuid);
 | |
|   if (GuidHob == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   PlatformInfoHob = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob);
 | |
| 
 | |
|   if (!PlatformInfoHob->QemuFwCfgChecked) {
 | |
|     QemuFwCfgProbe (
 | |
|       &PlatformInfoHob->QemuFwCfgSupported,
 | |
|       &PlatformInfoHob->QemuFwCfgDmaSupported
 | |
|       );
 | |
|     PlatformInfoHob->QemuFwCfgChecked = TRUE;
 | |
|   }
 | |
| 
 | |
|   return PlatformInfoHob;
 | |
| }
 | |
| 
 | |
| RETURN_STATUS
 | |
| EFIAPI
 | |
| QemuFwCfgInitialize (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   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
 | |
|   )
 | |
| {
 | |
|   EFI_HOB_PLATFORM_INFO  *PlatformInfoHob = QemuFwCfgGetPlatformInfo ();
 | |
| 
 | |
|   return PlatformInfoHob->QemuFwCfgSupported;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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
 | |
|   )
 | |
| {
 | |
|   EFI_HOB_PLATFORM_INFO  *PlatformInfoHob = QemuFwCfgGetPlatformInfo ();
 | |
| 
 | |
|   return PlatformInfoHob->QemuFwCfgDmaSupported;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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  Access;
 | |
|   UINT32                      AccessHigh, AccessLow;
 | |
|   UINT32                      Status;
 | |
| 
 | |
|   ASSERT (
 | |
|     Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
 | |
|     Control == FW_CFG_DMA_CTL_SKIP
 | |
|     );
 | |
| 
 | |
|   if (Size == 0) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // TDX does not support DMA operations in PEI stage, we should
 | |
|   // not have reached here.
 | |
|   //
 | |
|   ASSERT (!QemuFwCfgIsCcGuest ());
 | |
| 
 | |
|   Access.Control = SwapBytes32 (Control);
 | |
|   Access.Length  = SwapBytes32 (Size);
 | |
|   Access.Address = SwapBytes64 ((UINTN)Buffer);
 | |
| 
 | |
|   //
 | |
|   // 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 ();
 | |
| }
 |