]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
ArmVirtPkg/QemuFwCfgLib: Relocate QemuFwCfgLib to OvmfPkg
[mirror_edk2.git] / OvmfPkg / Library / QemuFwCfgLib / QemuFwCfgLibMmio.c
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
new file mode 100644 (file)
index 0000000..e2ac410
--- /dev/null
@@ -0,0 +1,633 @@
+/** @file\r
+\r
+  Stateful and implicitly initialized fw_cfg library implementation.\r
+\r
+  Copyright (C) 2013 - 2014, Red Hat, Inc.\r
+  Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/QemuFwCfgLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include <Protocol/FdtClient.h>\r
+\r
+STATIC UINTN mFwCfgSelectorAddress;\r
+STATIC UINTN mFwCfgDataAddress;\r
+STATIC UINTN mFwCfgDmaAddress;\r
+\r
+/**\r
+  Reads firmware configuration bytes into a buffer\r
+\r
+  @param[in] Size    Size in bytes to read\r
+  @param[in] Buffer  Buffer to store data into  (OPTIONAL if Size is 0)\r
+\r
+**/\r
+typedef\r
+VOID (EFIAPI READ_BYTES_FUNCTION) (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  );\r
+\r
+/**\r
+  Writes bytes from a buffer to firmware configuration\r
+\r
+  @param[in] Size    Size in bytes to write\r
+  @param[in] Buffer  Buffer to transfer data from (OPTIONAL if Size is 0)\r
+\r
+**/\r
+typedef\r
+VOID (EFIAPI WRITE_BYTES_FUNCTION) (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  );\r
+\r
+/**\r
+  Skips bytes in firmware configuration\r
+\r
+  @param[in] Size  Size in bytes to skip\r
+\r
+**/\r
+typedef\r
+VOID (EFIAPI SKIP_BYTES_FUNCTION) (\r
+  IN UINTN Size\r
+  );\r
+\r
+//\r
+// Forward declaration of the two implementations we have.\r
+//\r
+STATIC READ_BYTES_FUNCTION MmioReadBytes;\r
+STATIC WRITE_BYTES_FUNCTION MmioWriteBytes;\r
+STATIC SKIP_BYTES_FUNCTION MmioSkipBytes;\r
+STATIC READ_BYTES_FUNCTION DmaReadBytes;\r
+STATIC WRITE_BYTES_FUNCTION DmaWriteBytes;\r
+STATIC SKIP_BYTES_FUNCTION DmaSkipBytes;\r
+\r
+//\r
+// These correspond to the implementation we detect at runtime.\r
+//\r
+STATIC READ_BYTES_FUNCTION *InternalQemuFwCfgReadBytes = MmioReadBytes;\r
+STATIC WRITE_BYTES_FUNCTION *InternalQemuFwCfgWriteBytes = MmioWriteBytes;\r
+STATIC SKIP_BYTES_FUNCTION *InternalQemuFwCfgSkipBytes = MmioSkipBytes;\r
+\r
+\r
+/**\r
+  Returns a boolean indicating if the firmware configuration interface\r
+  is available or not.\r
+\r
+  This function may change fw_cfg state.\r
+\r
+  @retval TRUE   The interface is available\r
+  @retval FALSE  The interface is not available\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+QemuFwCfgIsAvailable (\r
+  VOID\r
+  )\r
+{\r
+  return (BOOLEAN)(mFwCfgSelectorAddress != 0 && mFwCfgDataAddress != 0);\r
+}\r
+\r
+\r
+RETURN_STATUS\r
+EFIAPI\r
+QemuFwCfgInitialize (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  FDT_CLIENT_PROTOCOL           *FdtClient;\r
+  CONST UINT64                  *Reg;\r
+  UINT32                        RegSize;\r
+  UINTN                         AddressCells, SizeCells;\r
+  UINT64                        FwCfgSelectorAddress;\r
+  UINT64                        FwCfgSelectorSize;\r
+  UINT64                        FwCfgDataAddress;\r
+  UINT64                        FwCfgDataSize;\r
+  UINT64                        FwCfgDmaAddress;\r
+  UINT64                        FwCfgDmaSize;\r
+\r
+  Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
+                  (VOID **)&FdtClient);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = FdtClient->FindCompatibleNodeReg (FdtClient, "qemu,fw-cfg-mmio",\r
+                         (CONST VOID **)&Reg, &AddressCells, &SizeCells,\r
+                         &RegSize);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_WARN,\r
+      "%a: No 'qemu,fw-cfg-mmio' compatible DT node found (Status == %r)\n",\r
+      __FUNCTION__, Status));\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ASSERT (AddressCells == 2);\r
+  ASSERT (SizeCells == 2);\r
+  ASSERT (RegSize == 2 * sizeof (UINT64));\r
+\r
+  FwCfgDataAddress     = SwapBytes64 (Reg[0]);\r
+  FwCfgDataSize        = 8;\r
+  FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
+  FwCfgSelectorSize    = 2;\r
+\r
+  //\r
+  // The following ASSERT()s express\r
+  //\r
+  //   Address + Size - 1 <= MAX_UINTN\r
+  //\r
+  // for both registers, that is, that the last byte in each MMIO range is\r
+  // expressible as a MAX_UINTN. The form below is mathematically\r
+  // equivalent, and it also prevents any unsigned overflow before the\r
+  // comparison.\r
+  //\r
+  ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
+  ASSERT (FwCfgDataAddress     <= MAX_UINTN - FwCfgDataSize     + 1);\r
+\r
+  mFwCfgSelectorAddress = FwCfgSelectorAddress;\r
+  mFwCfgDataAddress     = FwCfgDataAddress;\r
+\r
+  DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
+    FwCfgDataAddress));\r
+\r
+  if (SwapBytes64 (Reg[1]) >= 0x18) {\r
+    FwCfgDmaAddress = FwCfgDataAddress + 0x10;\r
+    FwCfgDmaSize    = 0x08;\r
+\r
+    //\r
+    // See explanation above.\r
+    //\r
+    ASSERT (FwCfgDmaAddress <= MAX_UINTN - FwCfgDmaSize + 1);\r
+\r
+    DEBUG ((EFI_D_INFO, "Found FwCfg DMA @ 0x%Lx\n", FwCfgDmaAddress));\r
+  } else {\r
+    FwCfgDmaAddress = 0;\r
+  }\r
+\r
+  if (QemuFwCfgIsAvailable ()) {\r
+    UINT32 Signature;\r
+\r
+    QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
+    Signature = QemuFwCfgRead32 ();\r
+    if (Signature == SIGNATURE_32 ('Q', 'E', 'M', 'U')) {\r
+      //\r
+      // For DMA support, we require the DTB to advertise the register, and the\r
+      // feature bitmap (which we read without DMA) to confirm the feature.\r
+      //\r
+      if (FwCfgDmaAddress != 0) {\r
+        UINT32 Features;\r
+\r
+        QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
+        Features = QemuFwCfgRead32 ();\r
+        if ((Features & FW_CFG_F_DMA) != 0) {\r
+          mFwCfgDmaAddress = FwCfgDmaAddress;\r
+          InternalQemuFwCfgReadBytes = DmaReadBytes;\r
+          InternalQemuFwCfgWriteBytes = DmaWriteBytes;\r
+          InternalQemuFwCfgSkipBytes = DmaSkipBytes;\r
+        }\r
+      }\r
+    } else {\r
+      mFwCfgSelectorAddress = 0;\r
+      mFwCfgDataAddress     = 0;\r
+    }\r
+  }\r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Selects a firmware configuration item for reading.\r
+\r
+  Following this call, any data read from this item will start from the\r
+  beginning of the configuration item's data.\r
+\r
+  @param[in] QemuFwCfgItem  Firmware Configuration item to read\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+QemuFwCfgSelectItem (\r
+  IN FIRMWARE_CONFIG_ITEM QemuFwCfgItem\r
+  )\r
+{\r
+  if (QemuFwCfgIsAvailable ()) {\r
+    MmioWrite16 (mFwCfgSelectorAddress, SwapBytes16 ((UINT16)QemuFwCfgItem));\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Slow READ_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+MmioReadBytes (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  )\r
+{\r
+  UINTN Left;\r
+  UINT8 *Ptr;\r
+  UINT8 *End;\r
+\r
+#ifdef MDE_CPU_AARCH64\r
+  Left = Size & 7;\r
+#else\r
+  Left = Size & 3;\r
+#endif\r
+\r
+  Size -= Left;\r
+  Ptr = Buffer;\r
+  End = Ptr + Size;\r
+\r
+#ifdef MDE_CPU_AARCH64\r
+  while (Ptr < End) {\r
+    *(UINT64 *)Ptr = MmioRead64 (mFwCfgDataAddress);\r
+    Ptr += 8;\r
+  }\r
+  if (Left & 4) {\r
+    *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
+    Ptr += 4;\r
+  }\r
+#else\r
+  while (Ptr < End) {\r
+    *(UINT32 *)Ptr = MmioRead32 (mFwCfgDataAddress);\r
+    Ptr += 4;\r
+  }\r
+#endif\r
+\r
+  if (Left & 2) {\r
+    *(UINT16 *)Ptr = MmioRead16 (mFwCfgDataAddress);\r
+    Ptr += 2;\r
+  }\r
+  if (Left & 1) {\r
+    *Ptr = MmioRead8 (mFwCfgDataAddress);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Transfer an array of bytes, or skip a number of bytes, using the DMA\r
+  interface.\r
+\r
+  @param[in]     Size     Size in bytes to transfer or skip.\r
+\r
+  @param[in,out] Buffer   Buffer to read data into or write data from. Ignored,\r
+                          and may be NULL, if Size is zero, or Control is\r
+                          FW_CFG_DMA_CTL_SKIP.\r
+\r
+  @param[in]     Control  One of the following:\r
+                          FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.\r
+                          FW_CFG_DMA_CTL_READ  - read from fw_cfg into Buffer.\r
+                          FW_CFG_DMA_CTL_SKIP  - skip bytes in fw_cfg.\r
+**/\r
+STATIC\r
+VOID\r
+DmaTransferBytes (\r
+  IN     UINTN  Size,\r
+  IN OUT VOID   *Buffer OPTIONAL,\r
+  IN     UINT32 Control\r
+  )\r
+{\r
+  volatile FW_CFG_DMA_ACCESS Access;\r
+  UINT32                     Status;\r
+\r
+  ASSERT (Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||\r
+    Control == FW_CFG_DMA_CTL_SKIP);\r
+\r
+  if (Size == 0) {\r
+    return;\r
+  }\r
+\r
+  ASSERT (Size <= MAX_UINT32);\r
+\r
+  Access.Control = SwapBytes32 (Control);\r
+  Access.Length  = SwapBytes32 ((UINT32)Size);\r
+  Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);\r
+\r
+  //\r
+  // We shouldn't start the transfer before setting up Access.\r
+  //\r
+  MemoryFence ();\r
+\r
+  //\r
+  // This will fire off the transfer.\r
+  //\r
+#ifdef MDE_CPU_AARCH64\r
+  MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));\r
+#else\r
+  MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));\r
+#endif\r
+\r
+  //\r
+  // We shouldn't look at Access.Control before starting the transfer.\r
+  //\r
+  MemoryFence ();\r
+\r
+  do {\r
+    Status = SwapBytes32 (Access.Control);\r
+    ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
+  } while (Status != 0);\r
+\r
+  //\r
+  // The caller will want to access the transferred data.\r
+  //\r
+  MemoryFence ();\r
+}\r
+\r
+\r
+/**\r
+  Fast READ_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+DmaReadBytes (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  )\r
+{\r
+  DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_READ);\r
+}\r
+\r
+\r
+/**\r
+  Reads firmware configuration bytes into a buffer\r
+\r
+  If called multiple times, then the data read will continue at the offset of\r
+  the firmware configuration item where the previous read ended.\r
+\r
+  @param[in] Size    Size in bytes to read\r
+  @param[in] Buffer  Buffer to store data into\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+QemuFwCfgReadBytes (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer\r
+  )\r
+{\r
+  if (QemuFwCfgIsAvailable ()) {\r
+    InternalQemuFwCfgReadBytes (Size, Buffer);\r
+  } else {\r
+    ZeroMem (Buffer, Size);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Slow WRITE_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+MmioWriteBytes (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  )\r
+{\r
+  UINTN Idx;\r
+\r
+  for (Idx = 0; Idx < Size; ++Idx) {\r
+    MmioWrite8 (mFwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Fast WRITE_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+DmaWriteBytes (\r
+  IN UINTN Size,\r
+  IN VOID  *Buffer OPTIONAL\r
+  )\r
+{\r
+  DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_WRITE);\r
+}\r
+\r
+\r
+/**\r
+  Write firmware configuration bytes from a buffer\r
+\r
+  If called multiple times, then the data written will continue at the offset\r
+  of the firmware configuration item where the previous write ended.\r
+\r
+  @param[in] Size    Size in bytes to write\r
+  @param[in] Buffer  Buffer to read data from\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+QemuFwCfgWriteBytes (\r
+  IN UINTN                  Size,\r
+  IN VOID                   *Buffer\r
+  )\r
+{\r
+  if (QemuFwCfgIsAvailable ()) {\r
+    InternalQemuFwCfgWriteBytes (Size, Buffer);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Slow SKIP_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+MmioSkipBytes (\r
+  IN UINTN Size\r
+  )\r
+{\r
+  UINTN ChunkSize;\r
+  UINT8 SkipBuffer[256];\r
+\r
+  //\r
+  // Emulate the skip by reading data in chunks, and throwing it away. The\r
+  // implementation below doesn't affect the static data footprint for client\r
+  // modules. Large skips are not expected, therefore this fallback is not\r
+  // performance critical. The size of SkipBuffer is thought not to exert a\r
+  // large pressure on the stack.\r
+  //\r
+  while (Size > 0) {\r
+    ChunkSize = MIN (Size, sizeof SkipBuffer);\r
+    MmioReadBytes (ChunkSize, SkipBuffer);\r
+    Size -= ChunkSize;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Fast SKIP_BYTES_FUNCTION.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+DmaSkipBytes (\r
+  IN UINTN Size\r
+  )\r
+{\r
+  DmaTransferBytes (Size, NULL, FW_CFG_DMA_CTL_SKIP);\r
+}\r
+\r
+\r
+/**\r
+  Skip bytes in the firmware configuration item.\r
+\r
+  Increase the offset of the firmware configuration item without transferring\r
+  bytes between the item and a caller-provided buffer. Subsequent read, write\r
+  or skip operations will commence at the increased offset.\r
+\r
+  @param[in] Size  Number of bytes to skip.\r
+**/\r
+VOID\r
+EFIAPI\r
+QemuFwCfgSkipBytes (\r
+  IN UINTN Size\r
+  )\r
+{\r
+  if (QemuFwCfgIsAvailable ()) {\r
+    InternalQemuFwCfgSkipBytes (Size);\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Reads a UINT8 firmware configuration value\r
+\r
+  @return  Value of Firmware Configuration item read\r
+\r
+**/\r
+UINT8\r
+EFIAPI\r
+QemuFwCfgRead8 (\r
+  VOID\r
+  )\r
+{\r
+  UINT8 Result;\r
+\r
+  QemuFwCfgReadBytes (sizeof Result, &Result);\r
+  return Result;\r
+}\r
+\r
+\r
+/**\r
+  Reads a UINT16 firmware configuration value\r
+\r
+  @return  Value of Firmware Configuration item read\r
+\r
+**/\r
+UINT16\r
+EFIAPI\r
+QemuFwCfgRead16 (\r
+  VOID\r
+  )\r
+{\r
+  UINT16 Result;\r
+\r
+  QemuFwCfgReadBytes (sizeof Result, &Result);\r
+  return Result;\r
+}\r
+\r
+\r
+/**\r
+  Reads a UINT32 firmware configuration value\r
+\r
+  @return  Value of Firmware Configuration item read\r
+\r
+**/\r
+UINT32\r
+EFIAPI\r
+QemuFwCfgRead32 (\r
+  VOID\r
+  )\r
+{\r
+  UINT32 Result;\r
+\r
+  QemuFwCfgReadBytes (sizeof Result, &Result);\r
+  return Result;\r
+}\r
+\r
+\r
+/**\r
+  Reads a UINT64 firmware configuration value\r
+\r
+  @return  Value of Firmware Configuration item read\r
+\r
+**/\r
+UINT64\r
+EFIAPI\r
+QemuFwCfgRead64 (\r
+  VOID\r
+  )\r
+{\r
+  UINT64 Result;\r
+\r
+  QemuFwCfgReadBytes (sizeof Result, &Result);\r
+  return Result;\r
+}\r
+\r
+\r
+/**\r
+  Find the configuration item corresponding to the firmware configuration file.\r
+\r
+  @param[in]  Name  Name of file to look up.\r
+  @param[out] Item  Configuration item corresponding to the file, to be passed\r
+                    to QemuFwCfgSelectItem ().\r
+  @param[out] Size  Number of bytes in the file.\r
+\r
+  @retval RETURN_SUCCESS      If file is found.\r
+  @retval RETURN_NOT_FOUND    If file is not found.\r
+  @retval RETURN_UNSUPPORTED  If firmware configuration is unavailable.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+QemuFwCfgFindFile (\r
+  IN   CONST CHAR8           *Name,\r
+  OUT  FIRMWARE_CONFIG_ITEM  *Item,\r
+  OUT  UINTN                 *Size\r
+  )\r
+{\r
+  UINT32 Count;\r
+  UINT32 Idx;\r
+\r
+  if (!QemuFwCfgIsAvailable ()) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemFileDir);\r
+  Count = SwapBytes32 (QemuFwCfgRead32 ());\r
+\r
+  for (Idx = 0; Idx < Count; ++Idx) {\r
+    UINT32 FileSize;\r
+    UINT16 FileSelect;\r
+    CHAR8  FName[QEMU_FW_CFG_FNAME_SIZE];\r
+\r
+    FileSize   = QemuFwCfgRead32 ();\r
+    FileSelect = QemuFwCfgRead16 ();\r
+    QemuFwCfgRead16 (); // skip the field called "reserved"\r
+    InternalQemuFwCfgReadBytes (sizeof (FName), FName);\r
+\r
+    if (AsciiStrCmp (Name, FName) == 0) {\r
+      *Item = (FIRMWARE_CONFIG_ITEM) SwapBytes16 (FileSelect);\r
+      *Size = SwapBytes32 (FileSize);\r
+      return RETURN_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return RETURN_NOT_FOUND;\r
+}\r