The protocol is documented in "docs/specs/fw_cfg.txt" in the QEMU tree.
Cc: Ard Biesheuvel <[email protected]> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek <[email protected]> --- ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf | 2 + ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c | 126 ++++++++++++++++++-- 2 files changed, 121 insertions(+), 7 deletions(-) diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf index 42f21f2..298aa6e 100644 --- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf +++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf @@ -44,9 +44,11 @@ [Packages] [LibraryClasses] BaseLib BaseMemoryLib + DebugLib IoLib PcdLib [Pcd] gArmVirtTokenSpaceGuid.PcdFwCfgSelectorAddress gArmVirtTokenSpaceGuid.PcdFwCfgDataAddress + gArmVirtTokenSpaceGuid.PcdFwCfgDmaAddress diff --git a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c index c62eee3..303dc52 100644 --- a/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c +++ b/ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c @@ -16,12 +16,58 @@ #include <Library/BaseLib.h> #include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> #include <Library/IoLib.h> #include <Library/PcdLib.h> #include <Library/QemuFwCfgLib.h> 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 ) @@ -163,6 +220,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 If called multiple times, then the data read will continue at the offset of -- 1.8.3.1 _______________________________________________ edk2-devel mailing list [email protected] https://lists.01.org/mailman/listinfo/edk2-devel

