diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c index 804d5b0e42..0bbf121de4 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLib.c @@ -98,6 +98,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 @@ -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); } } diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h index 5b162bf987..6e87c62510 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibInternal.h @@ -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 diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiDxe.c index 88d88c0edf..ac05f4c347 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiDxe.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgPeiDxe.c @@ -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; +} diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c index 56c59ca3f0..465ccbe90d 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgSec.c @@ -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; +}