]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/VirtioNetDxe: add Tx packet map/unmap helper functions
authorBrijesh Singh <brijesh.singh@amd.com>
Thu, 14 Sep 2017 21:22:45 +0000 (16:22 -0500)
committerLaszlo Ersek <lersek@redhat.com>
Thu, 14 Sep 2017 21:54:15 +0000 (23:54 +0200)
When device is behind IOMMU, driver is require to pass the device address
of TxBuf in the Tx VRING. The patch adds helper functions and data
structure to map and unmap the TxBuf system physical address to a device
address.

Since the TxBuf is returned back to caller from VirtioNetGetStatus() hence
we use OrderedCollection interface to save the TxBuf system physical to
device address mapping. After the TxBuf is succesfully transmitted
VirtioNetUnmapTxBuf() does the reverse lookup in OrderedCollection data
structure to get the system physical address of TxBuf for a given device
address.

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c
OvmfPkg/VirtioNetDxe/VirtioNet.h
OvmfPkg/VirtioNetDxe/VirtioNet.inf

index 9621f936d2cba243a9e4aad8ad18299ba999d937..ffb3deefe00c2518a5a028d00420fe11d4aff23b 100644 (file)
@@ -147,7 +147,8 @@ ReleaseQueue:
                            EfiSimpleNetworkInitialized state.\r
 \r
   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the stack to track the heads\r
-                                of free descriptor chains.\r
+                                of free descriptor chains or failed to init\r
+                                TxBufCollection.\r
   @return                       Status codes from VIRTIO_DEVICE_PROTOCOL.\r
                                 AllocateSharedPages() or\r
                                 VirtioMapAllBytesInSharedBuffer()\r
@@ -176,6 +177,15 @@ VirtioNetInitTx (
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
+  Dev->TxBufCollection = OrderedCollectionInit (\r
+                           VirtioNetTxBufMapInfoCompare,\r
+                           VirtioNetTxBufDeviceAddressCompare\r
+                           );\r
+  if (Dev->TxBufCollection == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeTxFreeStack;\r
+  }\r
+\r
   //\r
   // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it\r
   // can be accessed equally by both processor and device.\r
@@ -186,7 +196,7 @@ VirtioNetInitTx (
                           &TxSharedReqBuffer\r
                           );\r
   if (EFI_ERROR (Status)) {\r
-    goto FreeTxFreeStack;\r
+    goto UninitTxBufCollection;\r
   }\r
 \r
   ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);\r
@@ -267,6 +277,10 @@ FreeTxSharedReqBuffer:
                  EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),\r
                  TxSharedReqBuffer\r
                  );\r
+\r
+UninitTxBufCollection:\r
+  OrderedCollectionUninit (Dev->TxBufCollection);\r
+\r
 FreeTxFreeStack:\r
   FreePool (Dev->TxFreeStack);\r
 \r
index 2fce8142d554d7849715df88528c298707ca423c..18dbf1812541b809de94b23c58abb75a2baafe45 100644 (file)
 \r
 #include "VirtioNet.h"\r
 \r
+//\r
+// The user structure for the ordered collection that will track the mapping\r
+// info of the packets queued in TxRing\r
+//\r
+typedef struct {\r
+  VOID                  *Buffer;\r
+  EFI_PHYSICAL_ADDRESS  DeviceAddress;  // lookup key for reverse mapping\r
+  VOID                  *BufMap;\r
+} TX_BUF_MAP_INFO;\r
+\r
 /**\r
   Release RX and TX resources on the boundary of the\r
   EfiSimpleNetworkInitialized state.\r
@@ -54,6 +64,10 @@ VirtioNetShutdownTx (
   IN OUT VNET_DEV *Dev\r
   )\r
 {\r
+  ORDERED_COLLECTION_ENTRY *Entry, *Entry2;\r
+  TX_BUF_MAP_INFO          *TxBufMapInfo;\r
+  VOID                     *UserStruct;\r
+\r
   Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->TxSharedReqMap);\r
   Dev->VirtIo->FreeSharedPages (\r
                  Dev->VirtIo,\r
@@ -61,6 +75,17 @@ VirtioNetShutdownTx (
                  Dev->TxSharedReq\r
                  );\r
 \r
+  for (Entry = OrderedCollectionMin (Dev->TxBufCollection);\r
+       Entry != NULL;\r
+       Entry = Entry2) {\r
+    Entry2 = OrderedCollectionNext (Entry);\r
+    OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);\r
+    TxBufMapInfo = UserStruct;\r
+    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);\r
+    FreePool (TxBufMapInfo);\r
+  }\r
+  OrderedCollectionUninit (Dev->TxBufCollection);\r
+\r
   FreePool (Dev->TxFreeStack);\r
 }\r
 \r
@@ -83,3 +108,202 @@ VirtioNetUninitRing (
   Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RingMap);\r
   VirtioRingUninit (Dev->VirtIo, Ring);\r
 }\r
+\r
+\r
+/**\r
+  Map Caller-supplied TxBuf buffer to the device-mapped address\r
+\r
+  @param[in]    Dev               The VNET_DEV driver instance which wants to\r
+                                  map the Tx packet.\r
+  @param[in]    Buffer            The system physical address of TxBuf\r
+  @param[in]    NumberOfBytes     Number of bytes to map\r
+  @param[out]   DeviceAddress     The resulting device address for the bus\r
+                                  master access.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to\r
+                                  a lack of resources.\r
+  @return                         Status codes from\r
+                                  VirtioMapAllBytesInSharedBuffer()\r
+  @retval EFI_SUCCESS             Caller-supplied buffer is succesfully mapped.\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioNetMapTxBuf (\r
+  IN  VNET_DEV              *Dev,\r
+  IN  VOID                  *Buffer,\r
+  IN  UINTN                 NumberOfBytes,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  TX_BUF_MAP_INFO           *TxBufMapInfo;\r
+  EFI_PHYSICAL_ADDRESS      Address;\r
+  VOID                      *Mapping;\r
+\r
+  TxBufMapInfo = AllocatePool (sizeof (*TxBufMapInfo));\r
+  if (TxBufMapInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = VirtioMapAllBytesInSharedBuffer (\r
+             Dev->VirtIo,\r
+             VirtioOperationBusMasterRead,\r
+             Buffer,\r
+             NumberOfBytes,\r
+             &Address,\r
+             &Mapping\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeTxBufMapInfo;\r
+  }\r
+\r
+  TxBufMapInfo->Buffer = Buffer;\r
+  TxBufMapInfo->DeviceAddress = Address;\r
+  TxBufMapInfo->BufMap = Mapping;\r
+\r
+  Status = OrderedCollectionInsert (\r
+             Dev->TxBufCollection,\r
+             NULL,\r
+             TxBufMapInfo\r
+             );\r
+  switch (Status) {\r
+  case EFI_OUT_OF_RESOURCES:\r
+    goto UnmapTxBuf;\r
+  case EFI_ALREADY_STARTED:\r
+    //\r
+    // This should never happen: it implies\r
+    //\r
+    // - an identity-mapping VIRTIO_DEVICE_PROTOCOL.MapSharedBuffer()\r
+    //   implementation -- which is fine,\r
+    //\r
+    // - and an SNP client that queues multiple instances of the exact same\r
+    //   buffer address with SNP.Transmit() -- which is undefined behavior,\r
+    //   based on the TxBuf language in UEFI-2.7,\r
+    //   EFI_SIMPLE_NETWORK.GetStatus().\r
+    //\r
+    ASSERT (FALSE);\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto UnmapTxBuf;\r
+  default:\r
+    ASSERT_EFI_ERROR (Status);\r
+    break;\r
+  }\r
+\r
+  *DeviceAddress = Address;\r
+  return EFI_SUCCESS;\r
+\r
+UnmapTxBuf:\r
+  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);\r
+\r
+FreeTxBufMapInfo:\r
+  FreePool (TxBufMapInfo);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Unmap (aka reverse mapping) device mapped TxBuf buffer to the system\r
+  physical address\r
+\r
+  @param[in]    Dev               The VNET_DEV driver instance which wants to\r
+                                  reverse- and unmap the Tx packet.\r
+  @param[out]   Buffer            The system physical address of TxBuf\r
+  @param[in]    DeviceAddress     The device address for the TxBuf\r
+\r
+  @retval EFI_INVALID_PARAMETER   The DeviceAddress is not mapped\r
+  @retval EFI_SUCCESS             The TxBuf at DeviceAddress has been unmapped,\r
+                                  and Buffer has been set to TxBuf's system\r
+                                  physical address.\r
+\r
+*/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioNetUnmapTxBuf (\r
+  IN  VNET_DEV              *Dev,\r
+  OUT VOID                  **Buffer,\r
+  IN  EFI_PHYSICAL_ADDRESS  DeviceAddress\r
+  )\r
+{\r
+  ORDERED_COLLECTION_ENTRY  *Entry;\r
+  TX_BUF_MAP_INFO           *TxBufMapInfo;\r
+  VOID                      *UserStruct;\r
+\r
+  Entry = OrderedCollectionFind (Dev->TxBufCollection, &DeviceAddress);\r
+  if (Entry == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);\r
+\r
+  TxBufMapInfo = UserStruct;\r
+\r
+  *Buffer = TxBufMapInfo->Buffer;\r
+  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);\r
+  FreePool (TxBufMapInfo);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Comparator function for two TX_BUF_MAP_INFO objects.\r
+\r
+  @param[in] UserStruct1  Pointer to the first TX_BUF_MAP_INFO object.\r
+\r
+  @param[in] UserStruct2  Pointer to the second TX_BUF_MAP_INFO object.\r
+\r
+  @retval <0  If UserStruct1 compares less than UserStruct2.\r
+\r
+  @retval  0  If UserStruct1 compares equal to UserStruct2.\r
+\r
+  @retval >0  If UserStruct1 compares greater than UserStruct2.\r
+*/\r
+INTN\r
+EFIAPI\r
+VirtioNetTxBufMapInfoCompare (\r
+  IN CONST VOID *UserStruct1,\r
+  IN CONST VOID *UserStruct2\r
+  )\r
+{\r
+  CONST TX_BUF_MAP_INFO *MapInfo1;\r
+  CONST TX_BUF_MAP_INFO *MapInfo2;\r
+\r
+  MapInfo1 = UserStruct1;\r
+  MapInfo2 = UserStruct2;\r
+\r
+  return MapInfo1->DeviceAddress < MapInfo2->DeviceAddress ? -1 :\r
+         MapInfo1->DeviceAddress > MapInfo2->DeviceAddress ?  1 :\r
+         0;\r
+}\r
+\r
+/**\r
+  Compare a standalone DeviceAddress against a TX_BUF_MAP_INFO object\r
+  containing an embedded DeviceAddress.\r
+\r
+  @param[in] StandaloneKey  Pointer to DeviceAddress, which has type\r
+                            EFI_PHYSICAL_ADDRESS.\r
+\r
+  @param[in] UserStruct     Pointer to the TX_BUF_MAP_INFO object with the\r
+                            embedded DeviceAddress.\r
+\r
+  @retval <0  If StandaloneKey compares less than UserStruct's key.\r
+\r
+  @retval  0  If StandaloneKey compares equal to UserStruct's key.\r
+\r
+  @retval >0  If StandaloneKey compares greater than UserStruct's key.\r
+**/\r
+INTN\r
+EFIAPI\r
+VirtioNetTxBufDeviceAddressCompare (\r
+  IN CONST VOID *StandaloneKey,\r
+  IN CONST VOID *UserStruct\r
+  )\r
+{\r
+  CONST EFI_PHYSICAL_ADDRESS *DeviceAddress;\r
+  CONST TX_BUF_MAP_INFO      *MapInfo;\r
+\r
+  DeviceAddress = StandaloneKey;\r
+  MapInfo = UserStruct;\r
+\r
+  return *DeviceAddress < MapInfo->DeviceAddress ? -1 :\r
+         *DeviceAddress > MapInfo->DeviceAddress ?  1 :\r
+         0;\r
+}\r
index 027f75993e8e2f51120526b95d0bb622048fc474..3fc88cfb790e6b0b8478ebe13dfc9162b29a03c5 100644 (file)
@@ -26,6 +26,7 @@
 #include <Protocol/DevicePath.h>\r
 #include <Protocol/DriverBinding.h>\r
 #include <Protocol/SimpleNetwork.h>\r
+#include <Library/OrderedCollectionLib.h>\r
 \r
 #define VNET_SIG SIGNATURE_32 ('V', 'N', 'E', 'T')\r
 \r
@@ -100,6 +101,7 @@ typedef struct {
   VIRTIO_1_0_NET_REQ          *TxSharedReq;      // VirtioNetInitTx\r
   VOID                        *TxSharedReqMap;   // VirtioNetInitTx\r
   UINT16                      TxLastUsed;        // VirtioNetInitTx\r
+  ORDERED_COLLECTION          *TxBufCollection;  // VirtioNetInitTx\r
 } VNET_DEV;\r
 \r
 \r
@@ -280,6 +282,42 @@ VirtioNetUninitRing (
   IN     VOID     *RingMap\r
   );\r
 \r
+//\r
+// utility functions to map caller-supplied Tx buffer system physical address\r
+// to a device address and vice versa\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioNetMapTxBuf (\r
+  IN  VNET_DEV              *Dev,\r
+  IN  VOID                  *Buffer,\r
+  IN  UINTN                 NumberOfBytes,\r
+  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress\r
+  );\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioNetUnmapTxBuf (\r
+  IN  VNET_DEV              *Dev,\r
+  OUT VOID                  **Buffer,\r
+  IN  EFI_PHYSICAL_ADDRESS  DeviceAddress\r
+  );\r
+\r
+INTN\r
+EFIAPI\r
+VirtioNetTxBufMapInfoCompare (\r
+  IN CONST VOID *UserStruct1,\r
+  IN CONST VOID *UserStruct2\r
+  );\r
+\r
+INTN\r
+EFIAPI\r
+VirtioNetTxBufDeviceAddressCompare (\r
+  IN CONST VOID *StandaloneKey,\r
+  IN CONST VOID *UserStruct\r
+  );\r
+\r
+\r
 //\r
 // event callbacks\r
 //\r
index a855ad4ac154df11bfd28ba5eb253b90b87477b4..9ff6d87e619021ea3a0d57b2acc2d2d4073cda5e 100644 (file)
@@ -49,6 +49,7 @@
   DebugLib\r
   DevicePathLib\r
   MemoryAllocationLib\r
+  OrderedCollectionLib\r
   UefiBootServicesTableLib\r
   UefiDriverEntryPoint\r
   UefiLib\r