]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Library / QemuFwCfgLib / QemuFwCfgDxe.c
index f8eb03bc3a9a4d7188f5aa110086b0651c831f04..f5787cd24d380a5776539dc37fd60eaa027eb307 100644 (file)
@@ -6,13 +6,7 @@
   Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.<BR>\r
   Copyright (c) 2017, Advanced Micro Devices. All rights reserved.<BR>\r
 \r
-  This program and the accompanying materials are licensed and made available\r
-  under the terms and conditions of the BSD License which accompanies this\r
-  distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
-  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 **/\r
 \r
 #include <Uefi.h>\r
@@ -20,6 +14,8 @@
 #include <Protocol/IoMmu.h>\r
 \r
 #include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/IoLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/QemuFwCfgLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 \r
 #include "QemuFwCfgLibInternal.h"\r
 \r
-STATIC BOOLEAN mQemuFwCfgSupported = FALSE;\r
-STATIC BOOLEAN mQemuFwCfgDmaSupported;\r
-\r
-STATIC EDKII_IOMMU_PROTOCOL        *mIoMmuProtocol;\r
-/**\r
-\r
- Returns a boolean indicating whether SEV is enabled\r
+STATIC BOOLEAN  mQemuFwCfgSupported = FALSE;\r
+STATIC BOOLEAN  mQemuFwCfgDmaSupported;\r
 \r
- @retval    TRUE    SEV is enabled\r
- @retval    FALSE   SEV is disabled\r
-**/\r
-BOOLEAN\r
-InternalQemuFwCfgSevIsEnabled (\r
-  VOID\r
-  )\r
-{\r
-  return MemEncryptSevIsEnabled ();\r
-}\r
+STATIC EDKII_IOMMU_PROTOCOL  *mIoMmuProtocol;\r
 \r
 /**\r
   Returns a boolean indicating if the firmware configuration interface\r
@@ -65,33 +47,33 @@ QemuFwCfgIsAvailable (
   return InternalQemuFwCfgIsAvailable ();\r
 }\r
 \r
-\r
 RETURN_STATUS\r
 EFIAPI\r
 QemuFwCfgInitialize (\r
   VOID\r
   )\r
 {\r
-  UINT32 Signature;\r
-  UINT32 Revision;\r
+  UINT32  Signature;\r
+  UINT32  Revision;\r
 \r
   //\r
   // Enable the access routines while probing to see if it is supported.\r
   // For probing we always use the IO Port (IoReadFifo8()) access method.\r
   //\r
-  mQemuFwCfgSupported = TRUE;\r
+  mQemuFwCfgSupported    = TRUE;\r
   mQemuFwCfgDmaSupported = FALSE;\r
 \r
   QemuFwCfgSelectItem (QemuFwCfgItemSignature);\r
   Signature = QemuFwCfgRead32 ();\r
-  DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature));\r
+  DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));\r
   QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);\r
   Revision = QemuFwCfgRead32 ();\r
-  DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision));\r
+  DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));\r
   if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||\r
       (Revision < 1)\r
-     ) {\r
-    DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n"));\r
+      )\r
+  {\r
+    DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));\r
     mQemuFwCfgSupported = FALSE;\r
     return RETURN_SUCCESS;\r
   }\r
@@ -104,17 +86,24 @@ QemuFwCfgInitialize (
   }\r
 \r
   if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) {\r
-    EFI_STATUS   Status;\r
+    EFI_STATUS  Status;\r
 \r
     //\r
     // IoMmuDxe driver must have installed the IOMMU protocol. If we are not\r
     // able to locate the protocol then something must have gone wrong.\r
     //\r
-    Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **)&mIoMmuProtocol);\r
+    Status = gBS->LocateProtocol (\r
+                    &gEdkiiIoMmuProtocolGuid,\r
+                    NULL,\r
+                    (VOID **)&mIoMmuProtocol\r
+                    );\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((DEBUG_ERROR,\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
         "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n",\r
-        gEfiCallerBaseName, __FUNCTION__));\r
+        gEfiCallerBaseName,\r
+        __FUNCTION__\r
+        ));\r
       ASSERT (FALSE);\r
       CpuDeadLoop ();\r
     }\r
@@ -123,7 +112,6 @@ QemuFwCfgInitialize (
   return RETURN_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
   Returns a boolean indicating if the firmware configuration interface is\r
   available for library-internal purposes.\r
@@ -157,74 +145,349 @@ InternalQemuFwCfgDmaIsAvailable (
 }\r
 \r
 /**\r
- Allocate a bounce buffer for SEV DMA.\r
-\r
-  @param[in]     NumPage  Number of pages.\r
-  @param[out]    Buffer   Allocated DMA Buffer pointer\r
+  Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used\r
+  between Host and device to exchange the information. The buffer must be free'd\r
+  using FreeFwCfgDmaAccessBuffer ().\r
 \r
 **/\r
+STATIC\r
 VOID\r
-InternalQemuFwCfgSevDmaAllocateBuffer (\r
-  OUT    VOID     **Buffer,\r
-  IN     UINT32   NumPages\r
+AllocFwCfgDmaAccessBuffer (\r
+  OUT   VOID  **Access,\r
+  OUT   VOID  **MapInfo\r
   )\r
 {\r
-  EFI_STATUS    Status;\r
+  UINTN                 Size;\r
+  UINTN                 NumPages;\r
+  EFI_STATUS            Status;\r
+  VOID                  *HostAddress;\r
+  EFI_PHYSICAL_ADDRESS  DmaAddress;\r
+  VOID                  *Mapping;\r
 \r
-  ASSERT (mIoMmuProtocol != NULL);\r
+  Size     = sizeof (FW_CFG_DMA_ACCESS);\r
+  NumPages = EFI_SIZE_TO_PAGES (Size);\r
 \r
+  //\r
+  // As per UEFI spec, in order to map a host address with\r
+  // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU\r
+  // AllocateBuffer()\r
+  //\r
   Status = mIoMmuProtocol->AllocateBuffer (\r
-                            mIoMmuProtocol,\r
-                            0,\r
-                            EfiBootServicesData,\r
-                            NumPages,\r
-                            Buffer,\r
-                            EDKII_IOMMU_ATTRIBUTE_MEMORY_CACHED\r
-                          );\r
+                             mIoMmuProtocol,\r
+                             AllocateAnyPages,\r
+                             EfiBootServicesData,\r
+                             NumPages,\r
+                             &HostAddress,\r
+                             EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE\r
+                             );\r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((DEBUG_ERROR,\r
-      "%a:%a failed to allocate %u pages\n", gEfiCallerBaseName, __FUNCTION__,\r
-      NumPages));\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  //\r
+  // Avoid exposing stale data even temporarily: zero the area before mapping\r
+  // it.\r
+  //\r
+  ZeroMem (HostAddress, Size);\r
+\r
+  //\r
+  // Map the host buffer with BusMasterCommonBuffer64\r
+  //\r
+  Status = mIoMmuProtocol->Map (\r
+                             mIoMmuProtocol,\r
+                             EdkiiIoMmuOperationBusMasterCommonBuffer64,\r
+                             HostAddress,\r
+                             &Size,\r
+                             &DmaAddress,\r
+                             &Mapping\r
+                             );\r
+  if (EFI_ERROR (Status)) {\r
+    mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  if (Size < sizeof (FW_CFG_DMA_ACCESS)) {\r
+    mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
+    mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress);\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      (UINT64)sizeof (FW_CFG_DMA_ACCESS),\r
+      (UINT64)Size\r
+      ));\r
     ASSERT (FALSE);\r
     CpuDeadLoop ();\r
   }\r
 \r
-  DEBUG ((DEBUG_VERBOSE,\r
-    "%a:%a buffer 0x%Lx Pages %u\n", gEfiCallerBaseName, __FUNCTION__,\r
-    (UINT64)(UINTN)Buffer, NumPages));\r
+  *Access  = HostAddress;\r
+  *MapInfo = Mapping;\r
 }\r
 \r
 /**\r
- Free the DMA buffer allocated using InternalQemuFwCfgSevDmaAllocateBuffer\r
+  Function is to used for freeing the Access buffer allocated using\r
+  AllocFwCfgDmaAccessBuffer()\r
+\r
+**/\r
+STATIC\r
+VOID\r
+FreeFwCfgDmaAccessBuffer (\r
+  IN  VOID  *Access,\r
+  IN  VOID  *Mapping\r
+  )\r
+{\r
+  UINTN       NumPages;\r
+  EFI_STATUS  Status;\r
 \r
-  @param[in]     NumPage  Number of pages.\r
-  @param[in]     Buffer   DMA Buffer pointer\r
+  NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS));\r
+\r
+  Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to UnMap() Mapping 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      (UINT64)(UINTN)Mapping\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
+\r
+  Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to Free() 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      (UINT64)(UINTN)Access\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
+}\r
+\r
+/**\r
+  Function is used for mapping host address to device address. The buffer must\r
+  be unmapped with UnmapDmaDataBuffer ().\r
 \r
 **/\r
+STATIC\r
 VOID\r
-InternalQemuFwCfgSevDmaFreeBuffer (\r
-  IN     VOID     *Buffer,\r
-  IN     UINT32   NumPages\r
+MapFwCfgDmaDataBuffer (\r
+  IN  BOOLEAN               IsWrite,\r
+  IN  VOID                  *HostAddress,\r
+  IN  UINT32                Size,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress,\r
+  OUT VOID                  **MapInfo\r
   )\r
 {\r
-  EFI_STATUS    Status;\r
+  EFI_STATUS            Status;\r
+  UINTN                 NumberOfBytes;\r
+  VOID                  *Mapping;\r
+  EFI_PHYSICAL_ADDRESS  PhysicalAddress;\r
+\r
+  NumberOfBytes = Size;\r
+  Status        = mIoMmuProtocol->Map (\r
+                                    mIoMmuProtocol,\r
+                                    (IsWrite ?\r
+                                     EdkiiIoMmuOperationBusMasterRead64 :\r
+                                     EdkiiIoMmuOperationBusMasterWrite64),\r
+                                    HostAddress,\r
+                                    &NumberOfBytes,\r
+                                    &PhysicalAddress,\r
+                                    &Mapping\r
+                                    );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      (UINT64)(UINTN)HostAddress,\r
+      (UINT64)Size\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
 \r
-  ASSERT (mIoMmuProtocol != NULL);\r
+  if (NumberOfBytes < Size) {\r
+    mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      Size,\r
+      (UINT64)NumberOfBytes\r
+      ));\r
+    ASSERT (FALSE);\r
+    CpuDeadLoop ();\r
+  }\r
 \r
-  Status = mIoMmuProtocol->FreeBuffer (\r
-                            mIoMmuProtocol,\r
-                            NumPages,\r
-                            Buffer\r
-                          );\r
+  *DeviceAddress = PhysicalAddress;\r
+  *MapInfo       = Mapping;\r
+}\r
+\r
+STATIC\r
+VOID\r
+UnmapFwCfgDmaDataBuffer (\r
+  IN  VOID  *Mapping\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping);\r
   if (EFI_ERROR (Status)) {\r
-    DEBUG ((DEBUG_ERROR,\r
-      "%a:%a failed to free buffer 0x%Lx pages %u\n", gEfiCallerBaseName,\r
-      __FUNCTION__, (UINT64)(UINTN)Buffer, NumPages));\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a:%a failed to UnMap() Mapping 0x%Lx\n",\r
+      gEfiCallerBaseName,\r
+      __FUNCTION__,\r
+      (UINT64)(UINTN)Mapping\r
+      ));\r
     ASSERT (FALSE);\r
     CpuDeadLoop ();\r
   }\r
+}\r
 \r
-  DEBUG ((DEBUG_VERBOSE,\r
-    "%a:%a buffer 0x%Lx Pages %u\n", gEfiCallerBaseName,__FUNCTION__,\r
-    (UINT64)(UINTN)Buffer, NumPages));\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
+VOID\r
+InternalQemuFwCfgDmaBytes (\r
+  IN     UINT32  Size,\r
+  IN OUT VOID    *Buffer OPTIONAL,\r
+  IN     UINT32  Control\r
+  )\r
+{\r
+  volatile FW_CFG_DMA_ACCESS  LocalAccess;\r
+  volatile FW_CFG_DMA_ACCESS  *Access;\r
+  UINT32                      AccessHigh, AccessLow;\r
+  UINT32                      Status;\r
+  VOID                        *AccessMapping, *DataMapping;\r
+  VOID                        *DataBuffer;\r
+\r
+  ASSERT (\r
+    Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||\r
+    Control == FW_CFG_DMA_CTL_SKIP\r
+    );\r
+\r
+  if (Size == 0) {\r
+    return;\r
+  }\r
+\r
+  Access        = &LocalAccess;\r
+  AccessMapping = NULL;\r
+  DataMapping   = NULL;\r
+  DataBuffer    = Buffer;\r
+\r
+  //\r
+  // When SEV is enabled, map Buffer to DMA address before issuing the DMA\r
+  // request\r
+  //\r
+  if (MemEncryptSevIsEnabled ()) {\r
+    VOID                  *AccessBuffer;\r
+    EFI_PHYSICAL_ADDRESS  DataBufferAddress;\r
+\r
+    //\r
+    // Allocate DMA Access buffer\r
+    //\r
+    AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping);\r
+\r
+    Access = AccessBuffer;\r
+\r
+    //\r
+    // Map actual data buffer\r
+    //\r
+    if (Control != FW_CFG_DMA_CTL_SKIP) {\r
+      MapFwCfgDmaDataBuffer (\r
+        Control == FW_CFG_DMA_CTL_WRITE,\r
+        Buffer,\r
+        Size,\r
+        &DataBufferAddress,\r
+        &DataMapping\r
+        );\r
+\r
+      DataBuffer = (VOID *)(UINTN)DataBufferAddress;\r
+    }\r
+  }\r
+\r
+  Access->Control = SwapBytes32 (Control);\r
+  Access->Length  = SwapBytes32 (Size);\r
+  Access->Address = SwapBytes64 ((UINTN)DataBuffer);\r
+\r
+  //\r
+  // Delimit the transfer from (a) modifications to Access, (b) in case of a\r
+  // write, from writes to Buffer by the caller.\r
+  //\r
+  MemoryFence ();\r
+\r
+  //\r
+  // Start the transfer.\r
+  //\r
+  AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32);\r
+  AccessLow  = (UINT32)(UINTN)Access;\r
+  IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh));\r
+  IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow));\r
+\r
+  //\r
+  // Don't look at Access.Control before starting the transfer.\r
+  //\r
+  MemoryFence ();\r
+\r
+  //\r
+  // Wait for the transfer to complete.\r
+  //\r
+  do {\r
+    Status = SwapBytes32 (Access->Control);\r
+    ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);\r
+  } while (Status != 0);\r
+\r
+  //\r
+  // After a read, the caller will want to use Buffer.\r
+  //\r
+  MemoryFence ();\r
+\r
+  //\r
+  // If Access buffer was dynamically allocated then free it.\r
+  //\r
+  if (AccessMapping != NULL) {\r
+    FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping);\r
+  }\r
+\r
+  //\r
+  // If DataBuffer was mapped then unmap it.\r
+  //\r
+  if (DataMapping != NULL) {\r
+    UnmapFwCfgDmaDataBuffer (DataMapping);\r
+  }\r
 }\r