@@ -30,4 +30,17 @@ InternalQemuFwCfgIsAvailable (
VOID
);
+
+/**
+ 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
+ );
+
#endif
@@ -99,6 +99,70 @@ QemuFwCfgSelectItem (
/**
+ Transfer an array of bytes using the DMA interface.
+
+ @param[in] Size Size in bytes to transfer.
+ @param[in,out] Buffer Buffer to read data into or write data from. May be
+ NULL if Size is zero.
+ @param[in] Write TRUE if writing to fw_cfg from Buffer, FALSE if
+ reading from fw_cfg into Buffer.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+ IN UINT32 Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN BOOLEAN Write
+ )
+{
+ volatile FW_CFG_DMA_ACCESS Access;
+ UINT32 AccessHigh, AccessLow;
+ UINT32 Status;
+
+ if (Size == 0) {
+ return;
+ }
+
+ Access.Control = SwapBytes32 (
+ Write ? FW_CFG_DMA_CTL_WRITE : FW_CFG_DMA_CTL_READ
+ );
+ 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 (0x514, SwapBytes32 (AccessHigh));
+ IoWrite32 (0x518, 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 ();
+}
+
+
+/**
Reads firmware configuration bytes into a buffer
@param[in] Size - Size in bytes to read
@@ -112,6 +176,10 @@ InternalQemuFwCfgReadBytes (
IN VOID *Buffer OPTIONAL
)
{
+ if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
+ InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FALSE);
+ return;
+ }
IoReadFifo8 (0x511, Size, Buffer);
}
@@ -160,6 +228,10 @@ QemuFwCfgWriteBytes (
)
{
if (InternalQemuFwCfgIsAvailable ()) {
+ if (InternalQemuFwCfgDmaIsAvailable () && Size <= MAX_UINT32) {
+ InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, TRUE);
+ return;
+ }
IoWriteFifo8 (0x511, Size, Buffer);
}
}
@@ -20,6 +20,7 @@
#include "QemuFwCfgLibInternal.h"
STATIC BOOLEAN mQemuFwCfgSupported = FALSE;
+STATIC BOOLEAN mQemuFwCfgDmaSupported;
/**
@@ -53,8 +54,10 @@ QemuFwCfgInitialize (
//
// Enable the access routines while probing to see if it is supported.
+ // For probing we always use the IO Port (IoReadFifo8()) access method.
//
mQemuFwCfgSupported = TRUE;
+ mQemuFwCfgDmaSupported = FALSE;
QemuFwCfgSelectItem (QemuFwCfgItemSignature);
Signature = QemuFwCfgRead32 ();
@@ -70,7 +73,12 @@ QemuFwCfgInitialize (
return RETURN_SUCCESS;
}
- DEBUG ((EFI_D_INFO, "QemuFwCfg interface is supported.\n"));
+ if ((Revision & FW_CFG_F_DMA) == 0) {
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (IO Port) is supported.\n"));
+ } else {
+ mQemuFwCfgDmaSupported = TRUE;
+ DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n"));
+ }
return RETURN_SUCCESS;
}
@@ -91,3 +99,18 @@ InternalQemuFwCfgIsAvailable (
{
return mQemuFwCfgSupported;
}
+
+/**
+ 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
+ )
+{
+ return mQemuFwCfgDmaSupported;
+}
@@ -79,3 +79,18 @@ InternalQemuFwCfgIsAvailable (
//
return TRUE;
}
+
+/**
+ 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
+ )
+{
+ return FALSE;
+}