blob: da86a3c84c028bb88cd8a141825071cd60fa5e5f [file] [log] [blame]
/** @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 ();
}