From 953bcbcce425f56bf48ef029daea7ec9e6421f4f Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 24 Sep 2015 21:40:41 +0000 Subject: [PATCH] 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 Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Ard Biesheuvel git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18545 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Library/QemuFwCfgLib/QemuFwCfgLib.c | 126 +++++++++++++++++- .../Library/QemuFwCfgLib/QemuFwCfgLib.inf | 2 + 2 files changed, 121 insertions(+), 7 deletions(-) diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c index c62eee3be8..303dc520c6 100644 --- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c +++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c @@ -16,12 +16,58 @@ #include #include +#include #include #include #include STATIC UINTN mFwCfgSelectorAddress; 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); 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; mFwCfgDataAddress = 0; } @@ -108,16 +169,12 @@ QemuFwCfgSelectItem ( /** - 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) - + Slow READ_BYTES_FUNCTION. **/ STATIC VOID EFIAPI -InternalQemuFwCfgReadBytes ( +MmioReadBytes ( IN UINTN Size, 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 diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf index 42f21f20c0..298aa6edfb 100644 --- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf +++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf @@ -44,9 +44,11 @@ [LibraryClasses] BaseLib BaseMemoryLib + DebugLib IoLib PcdLib [Pcd] gArmVirtTokenSpaceGuid.PcdFwCfgSelectorAddress gArmVirtTokenSpaceGuid.PcdFwCfgDataAddress + gArmVirtTokenSpaceGuid.PcdFwCfgDmaAddress