ArmVirtPkg: QemuFwCfgLib: read bytes from fw-cfg with DMA when available

The protocol is documented in "docs/specs/fw_cfg.txt" in the QEMU tree.

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18545 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Laszlo Ersek 2015-09-24 21:40:41 +00:00 committed by lersek
parent 50b91449a3
commit 953bcbcce4
2 changed files with 121 additions and 7 deletions

View File

@ -16,12 +16,58 @@
#include <Library/BaseLib.h> #include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h> #include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h> #include <Library/IoLib.h>
#include <Library/PcdLib.h> #include <Library/PcdLib.h>
#include <Library/QemuFwCfgLib.h> #include <Library/QemuFwCfgLib.h>
STATIC UINTN mFwCfgSelectorAddress; STATIC UINTN mFwCfgSelectorAddress;
STATIC UINTN mFwCfgDataAddress; STATIC UINTN mFwCfgDataAddress;
STATIC UINTN mFwCfgDmaAddress;
/**
Reads firmware configuration bytes into a buffer
@param[in] Size Size in bytes to read
@param[in] Buffer Buffer to store data into (OPTIONAL if Size is 0)
**/
typedef
VOID (EFIAPI READ_BYTES_FUNCTION) (
IN UINTN Size,
IN VOID *Buffer OPTIONAL
);
//
// Forward declaration of the two implementations we have.
//
STATIC READ_BYTES_FUNCTION MmioReadBytes;
STATIC READ_BYTES_FUNCTION DmaReadBytes;
//
// This points to the one we detect at runtime.
//
STATIC READ_BYTES_FUNCTION *InternalQemuFwCfgReadBytes = MmioReadBytes;
//
// Communication structure for DmaReadBytes(). All fields are encoded in big
// endian.
//
#pragma pack (1)
typedef struct {
UINT32 Control;
UINT32 Length;
UINT64 Address;
} FW_CFG_DMA_ACCESS;
#pragma pack ()
//
// Macros for the FW_CFG_DMA_ACCESS.Control bitmap (in native encoding).
//
#define FW_CFG_DMA_CTL_ERROR BIT0
#define FW_CFG_DMA_CTL_READ BIT1
#define FW_CFG_DMA_CTL_SKIP BIT2
#define FW_CFG_DMA_CTL_SELECT BIT3
/** /**
@ -77,7 +123,22 @@ QemuFwCfgInitialize (
QemuFwCfgSelectItem (QemuFwCfgItemSignature); QemuFwCfgSelectItem (QemuFwCfgItemSignature);
Signature = QemuFwCfgRead32 (); Signature = QemuFwCfgRead32 ();
if (Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) { if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {
//
// For DMA support, we require the DTB to advertise the register, and the
// feature bitmap (which we read without DMA) to confirm the feature.
//
if (PcdGet64 (PcdFwCfgDmaAddress) != 0) {
UINT32 Features;
QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
Features = QemuFwCfgRead32 ();
if ((Features & BIT1) != 0) {
mFwCfgDmaAddress = PcdGet64 (PcdFwCfgDmaAddress);
InternalQemuFwCfgReadBytes = DmaReadBytes;
}
}
} else {
mFwCfgSelectorAddress = 0; mFwCfgSelectorAddress = 0;
mFwCfgDataAddress = 0; mFwCfgDataAddress = 0;
} }
@ -108,16 +169,12 @@ QemuFwCfgSelectItem (
/** /**
Reads firmware configuration bytes into a buffer Slow READ_BYTES_FUNCTION.
@param[in] Size Size in bytes to read
@param[in] Buffer Buffer to store data into (OPTIONAL if Size is 0)
**/ **/
STATIC STATIC
VOID VOID
EFIAPI EFIAPI
InternalQemuFwCfgReadBytes ( MmioReadBytes (
IN UINTN Size, IN UINTN Size,
IN VOID *Buffer OPTIONAL IN VOID *Buffer OPTIONAL
) )
@ -162,6 +219,61 @@ InternalQemuFwCfgReadBytes (
} }
/**
Fast READ_BYTES_FUNCTION.
**/
STATIC
VOID
EFIAPI
DmaReadBytes (
IN UINTN Size,
IN VOID *Buffer OPTIONAL
)
{
volatile FW_CFG_DMA_ACCESS Access;
UINT32 Status;
if (Size == 0) {
return;
}
ASSERT (Size <= MAX_UINT32);
Access.Control = SwapBytes32 (FW_CFG_DMA_CTL_READ);
Access.Length = SwapBytes32 ((UINT32)Size);
Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);
//
// We shouldn't start the transfer before setting up Access.
//
MemoryFence ();
//
// This will fire off the transfer.
//
#ifdef MDE_CPU_AARCH64
MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));
#else
MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));
#endif
//
// We shouldn't look at Access.Control before starting the transfer.
//
MemoryFence ();
do {
Status = SwapBytes32 (Access.Control);
ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
} while (Status != 0);
//
// The caller will want to access the transferred data.
//
MemoryFence ();
}
/** /**
Reads firmware configuration bytes into a buffer Reads firmware configuration bytes into a buffer

View File

@ -44,9 +44,11 @@
[LibraryClasses] [LibraryClasses]
BaseLib BaseLib
BaseMemoryLib BaseMemoryLib
DebugLib
IoLib IoLib
PcdLib PcdLib
[Pcd] [Pcd]
gArmVirtTokenSpaceGuid.PcdFwCfgSelectorAddress gArmVirtTokenSpaceGuid.PcdFwCfgSelectorAddress
gArmVirtTokenSpaceGuid.PcdFwCfgDataAddress gArmVirtTokenSpaceGuid.PcdFwCfgDataAddress
gArmVirtTokenSpaceGuid.PcdFwCfgDmaAddress