summaryrefslogtreecommitdiff
path: root/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
diff options
context:
space:
mode:
authorAbner Chang <abner.chang@hpe.com>2021-10-11 21:33:59 +0800
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2021-10-14 06:25:52 +0000
commitf8d0501ded2ad426cc5c6c1237ec83fc978f8373 (patch)
tree520ea029e453c180d42742d6c15ded1814b0011a /OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
parent47bd85e9f9e4d065bce8bfd36d5963a6090ddb8c (diff)
downloadedk2-f8d0501ded2ad426cc5c6c1237ec83fc978f8373.zip
edk2-f8d0501ded2ad426cc5c6c1237ec83fc978f8373.tar.gz
edk2-f8d0501ded2ad426cc5c6c1237ec83fc978f8373.tar.bz2
ArmVirtPkg/QemuFwCfgLib: Relocate QemuFwCfgLib to OvmfPkg
Relocate QemuFwCfgLib to OvmfPkg/Library/QemuFwCfgLib and rename it to QemuFwCfgLibMmio, this library is leverage by both ARM and RISC-V archs. Signed-off-by: Abner Chang <abner.chang@hpe.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Sami Mujawar <sami.mujawar@arm.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: Daniel Schaefer <daniel.schaefer@hpe.com> Cc: Sunil V L <sunilvl@ventanamicro.com> Reviewed-by: Daniel Schaefer <daniel.schaefer@hpe.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Jiewen Yao <jiewen.yao@intel.com>
Diffstat (limited to 'OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c')
-rw-r--r--OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
new file mode 100644
index 0000000..e2ac410
--- /dev/null
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
@@ -0,0 +1,633 @@
+/** @file
+
+ Stateful and implicitly initialized fw_cfg library implementation.
+
+ Copyright (C) 2013 - 2014, Red Hat, Inc.
+ Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/FdtClient.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
+ );
+
+/**
+ Writes bytes from a buffer to firmware configuration
+
+ @param[in] Size Size in bytes to write
+ @param[in] Buffer Buffer to transfer data from (OPTIONAL if Size is 0)
+
+**/
+typedef
+VOID (EFIAPI WRITE_BYTES_FUNCTION) (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ );
+
+/**
+ Skips bytes in firmware configuration
+
+ @param[in] Size Size in bytes to skip
+
+**/
+typedef
+VOID (EFIAPI SKIP_BYTES_FUNCTION) (
+ IN UINTN Size
+ );
+
+//
+// Forward declaration of the two implementations we have.
+//
+STATIC READ_BYTES_FUNCTION MmioReadBytes;
+STATIC WRITE_BYTES_FUNCTION MmioWriteBytes;
+STATIC SKIP_BYTES_FUNCTION MmioSkipBytes;
+STATIC READ_BYTES_FUNCTION DmaReadBytes;
+STATIC WRITE_BYTES_FUNCTION DmaWriteBytes;
+STATIC SKIP_BYTES_FUNCTION DmaSkipBytes;
+
+//
+// These correspond to the implementation we detect at runtime.
+//
+STATIC READ_BYTES_FUNCTION *InternalQemuFwCfgReadBytes = MmioReadBytes;
+STATIC WRITE_BYTES_FUNCTION *InternalQemuFwCfgWriteBytes = MmioWriteBytes;
+STATIC SKIP_BYTES_FUNCTION *InternalQemuFwCfgSkipBytes = MmioSkipBytes;
+
+
+/**
+ Returns a boolean indicating if the firmware configuration interface
+ is available or not.
+
+ This function may change fw_cfg state.
+
+ @retval TRUE The interface is available
+ @retval FALSE The interface is not available
+
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+ VOID
+ )
+{
+ return (BOOLEAN)(mFwCfgSelectorAddress != 0 && mFwCfgDataAddress != 0);
+}
+
+
+RETURN_STATUS
+EFIAPI
+QemuFwCfgInitialize (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ FDT_CLIENT_PROTOCOL *FdtClient;
+ CONST UINT64 *Reg;
+ UINT32 RegSize;
+ UINTN AddressCells, SizeCells;
+ UINT64 FwCfgSelectorAddress;
+ UINT64 FwCfgSelectorSize;
+ UINT64 FwCfgDataAddress;
+ UINT64 FwCfgDataSize;
+ UINT64 FwCfgDmaAddress;
+ UINT64 FwCfgDmaSize;
+
+ Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,
+ (VOID **)&FdtClient);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",
+ (CONST VOID **)&Reg, &AddressCells, &SizeCells,
+ &RegSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_WARN,
+ "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",
+ __FUNCTION__, Status));
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (AddressCells == 2);
+ ASSERT (SizeCells == 2);
+ ASSERT (RegSize == 2 * sizeof (UINT64));
+
+ FwCfgDataAddress = SwapBytes64 (Reg[0]);
+ FwCfgDataSize = 8;
+ FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;
+ FwCfgSelectorSize = 2;
+
+ //
+ // The following ASSERT()s express
+ //
+ // Address + Size - 1 <= MAX_UINTN
+ //
+ // for both registers, that is, that the last byte in each MMIO range is
+ // expressible as a MAX_UINTN. The form below is mathematically
+ // equivalent, and it also prevents any unsigned overflow before the
+ // comparison.
+ //
+ ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);
+ ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);
+
+ mFwCfgSelectorAddress = FwCfgSelectorAddress;
+ mFwCfgDataAddress = FwCfgDataAddress;
+
+ DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,
+ FwCfgDataAddress));
+
+ if (SwapBytes64 (Reg[1]) >= 0x18) {
+ FwCfgDmaAddress = FwCfgDataAddress + 0x10;
+ FwCfgDmaSize = 0x08;
+
+ //
+ // See explanation above.
+ //
+ ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);
+
+ DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));
+ } else {
+ FwCfgDmaAddress = 0;
+ }
+
+ if (QemuFwCfgIsAvailable ()) {
+ UINT32 Signature;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+ Signature = QemuFwCfgRead32 ();
+ 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 (FwCfgDmaAddress != 0) {
+ UINT32 Features;
+
+ QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+ Features = QemuFwCfgRead32 ();
+ if ((Features & FW_CFG_F_DMA) != 0) {
+ mFwCfgDmaAddress = FwCfgDmaAddress;
+ InternalQemuFwCfgReadBytes = DmaReadBytes;
+ InternalQemuFwCfgWriteBytes = DmaWriteBytes;
+ InternalQemuFwCfgSkipBytes = DmaSkipBytes;
+ }
+ }
+ } else {
+ mFwCfgSelectorAddress = 0;
+ mFwCfgDataAddress = 0;
+ }
+ }
+ return RETURN_SUCCESS;
+}
+
+
+/**
+ Selects a firmware configuration item for reading.
+
+ Following this call, any data read from this item will start from the
+ beginning of the configuration item's data.
+
+ @param[in] QemuFwCfgItem Firmware Configuration item to read
+
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+ IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem
+ )
+{
+ if (QemuFwCfgIsAvailable ()) {
+ MmioWrite16 (mFwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItem));
+ }
+}
+
+
+/**
+ Slow READ_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+MmioReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ UINTN Left;
+ UINT8 *Ptr;
+ UINT8 *End;
+
+#ifdef MDE_CPU_AARCH64
+ Left = Size & 7;
+#else
+ Left = Size & 3;
+#endif
+
+ Size -= Left;
+ Ptr = Buffer;
+ End = Ptr + Size;
+
+#ifdef MDE_CPU_AARCH64
+ while (Ptr < End) {
+ *(UINT64 *)Ptr = MmioRead64 (mFwCfgDataAddress);
+ Ptr += 8;
+ }
+ if (Left & 4) {
+ *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);
+ Ptr += 4;
+ }
+#else
+ while (Ptr < End) {
+ *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);
+ Ptr += 4;
+ }
+#endif
+
+ if (Left & 2) {
+ *(UINT16 *)Ptr = MmioRead16 (mFwCfgDataAddress);
+ Ptr += 2;
+ }
+ if (Left & 1) {
+ *Ptr = MmioRead8 (mFwCfgDataAddress);
+ }
+}
+
+
+/**
+ Transfer an array of bytes, or skip a number of bytes, using the DMA
+ interface.
+
+ @param[in] Size Size in bytes to transfer or skip.
+
+ @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
+ and may be NULL, if Size is zero, or Control is
+ FW_CFG_DMA_CTL_SKIP.
+
+ @param[in] Control One of the following:
+ FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+ FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
+ FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
+**/
+STATIC
+VOID
+DmaTransferBytes (
+ IN UINTN Size,
+ IN OUT VOID *Buffer OPTIONAL,
+ IN UINT32 Control
+ )
+{
+ volatile FW_CFG_DMA_ACCESS Access;
+ UINT32 Status;
+
+ ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
+ Control == FW_CFG_DMA_CTL_SKIP);
+
+ if (Size == 0) {
+ return;
+ }
+
+ ASSERT (Size <= MAX_UINT32);
+
+ Access.Control = SwapBytes32 (Control);
+ 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 ();
+}
+
+
+/**
+ Fast READ_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+DmaReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_READ);
+}
+
+
+/**
+ Reads firmware configuration bytes into a buffer
+
+ If called multiple times, then the data read will continue at the offset of
+ the firmware configuration item where the previous read ended.
+
+ @param[in] Size Size in bytes to read
+ @param[in] Buffer Buffer to store data into
+
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ if (QemuFwCfgIsAvailable ()) {
+ InternalQemuFwCfgReadBytes (Size, Buffer);
+ } else {
+ ZeroMem (Buffer, Size);
+ }
+}
+
+
+/**
+ Slow WRITE_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+MmioWriteBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ UINTN Idx;
+
+ for (Idx = 0; Idx < Size; ++Idx) {
+ MmioWrite8 (mFwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);
+ }
+}
+
+
+/**
+ Fast WRITE_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+DmaWriteBytes (
+ IN UINTN Size,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_WRITE);
+}
+
+
+/**
+ Write firmware configuration bytes from a buffer
+
+ If called multiple times, then the data written will continue at the offset
+ of the firmware configuration item where the previous write ended.
+
+ @param[in] Size Size in bytes to write
+ @param[in] Buffer Buffer to read data from
+
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+ IN UINTN Size,
+ IN VOID *Buffer
+ )
+{
+ if (QemuFwCfgIsAvailable ()) {
+ InternalQemuFwCfgWriteBytes (Size, Buffer);
+ }
+}
+
+
+/**
+ Slow SKIP_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+MmioSkipBytes (
+ IN UINTN Size
+ )
+{
+ UINTN ChunkSize;
+ UINT8 SkipBuffer[256];
+
+ //
+ // Emulate the skip by reading data in chunks, and throwing it away. The
+ // implementation below doesn't affect the static data footprint for client
+ // modules. Large skips are not expected, therefore this fallback is not
+ // performance critical. The size of SkipBuffer is thought not to exert a
+ // large pressure on the stack.
+ //
+ while (Size > 0) {
+ ChunkSize = MIN (Size, sizeof SkipBuffer);
+ MmioReadBytes (ChunkSize, SkipBuffer);
+ Size -= ChunkSize;
+ }
+}
+
+
+/**
+ Fast SKIP_BYTES_FUNCTION.
+**/
+STATIC
+VOID
+EFIAPI
+DmaSkipBytes (
+ IN UINTN Size
+ )
+{
+ DmaTransferBytes (Size, NULL, FW_CFG_DMA_CTL_SKIP);
+}
+
+
+/**
+ Skip bytes in the firmware configuration item.
+
+ Increase the offset of the firmware configuration item without transferring
+ bytes between the item and a caller-provided buffer. Subsequent read, write
+ or skip operations will commence at the increased offset.
+
+ @param[in] Size Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+ IN UINTN Size
+ )
+{
+ if (QemuFwCfgIsAvailable ()) {
+ InternalQemuFwCfgSkipBytes (Size);
+ }
+}
+
+
+/**
+ Reads a UINT8 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+ VOID
+ )
+{
+ UINT8 Result;
+
+ QemuFwCfgReadBytes (sizeof Result, &Result);
+ return Result;
+}
+
+
+/**
+ Reads a UINT16 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+ VOID
+ )
+{
+ UINT16 Result;
+
+ QemuFwCfgReadBytes (sizeof Result, &Result);
+ return Result;
+}
+
+
+/**
+ Reads a UINT32 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+ VOID
+ )
+{
+ UINT32 Result;
+
+ QemuFwCfgReadBytes (sizeof Result, &Result);
+ return Result;
+}
+
+
+/**
+ Reads a UINT64 firmware configuration value
+
+ @return Value of Firmware Configuration item read
+
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+ VOID
+ )
+{
+ UINT64 Result;
+
+ QemuFwCfgReadBytes (sizeof Result, &Result);
+ return Result;
+}
+
+
+/**
+ Find the configuration item corresponding to the firmware configuration file.
+
+ @param[in] Name Name of file to look up.
+ @param[out] Item Configuration item corresponding to the file, to be passed
+ to QemuFwCfgSelectItem ().
+ @param[out] Size Number of bytes in the file.
+
+ @retval RETURN_SUCCESS If file is found.
+ @retval RETURN_NOT_FOUND If file is not found.
+ @retval RETURN_UNSUPPORTED If firmware configuration is unavailable.
+
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+ IN CONST CHAR8 *Name,
+ OUT FIRMWARE_CONFIG_ITEM *Item,
+ OUT UINTN *Size
+ )
+{
+ UINT32 Count;
+ UINT32 Idx;
+
+ if (!QemuFwCfgIsAvailable ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
+ Count = SwapBytes32 (QemuFwCfgRead32 ());
+
+ for (Idx = 0; Idx < Count; ++Idx) {
+ UINT32 FileSize;
+ UINT16 FileSelect;
+ CHAR8 FName[QEMU_FW_CFG_FNAME_SIZE];
+
+ FileSize = QemuFwCfgRead32 ();
+ FileSelect = QemuFwCfgRead16 ();
+ QemuFwCfgRead16 (); // skip the field called "reserved"
+ InternalQemuFwCfgReadBytes (sizeof (FName), FName);
+
+ if (AsciiStrCmp (Name, FName) == 0) {
+ *Item = (FIRMWARE_CONFIG_ITEM) SwapBytes16 (FileSelect);
+ *Size = SwapBytes32 (FileSize);
+ return RETURN_SUCCESS;
+ }
+ }
+
+ return RETURN_NOT_FOUND;
+}