text: /** @file
  
    Helper functions used by at least two Simple Network Protocol methods.
  
    Copyright (C) 2013, Red Hat, Inc.
  
    SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include <Library/MemoryAllocationLib.h>
  
  #include "VirtioNet.h"
  
  //
  // The user structure for the ordered collection that will track the mapping
  // info of the packets queued in TxRing
  //
  typedef struct {
    VOID                  *Buffer;
    EFI_PHYSICAL_ADDRESS  DeviceAddress;  // lookup key for reverse mapping
    VOID                  *BufMap;
  } TX_BUF_MAP_INFO;
  
  /**
    Release RX and TX resources on the boundary of the
    EfiSimpleNetworkInitialized state.
  
    These functions contribute to rolling back a partial, failed initialization
    of the virtio-net SNP driver instance, or to shutting down a fully
    initialized, running instance.
  
    They are only callable by the VirtioNetInitialize() and the
    VirtioNetShutdown() SNP methods. See the state diagram in "VirtioNet.h".
  
    @param[in,out] Dev  The VNET_DEV driver instance being shut down, or whose
                        partial, failed initialization is being rolled back.
  */
  
  VOID
  EFIAPI
  VirtioNetShutdownRx (
    IN OUT VNET_DEV *Dev
    )
  {
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
    Dev->VirtIo->FreeSharedPages (
                   Dev->VirtIo,
                   Dev->RxBufNrPages,
                   Dev->RxBuf
                   );
  }
  
  
  VOID
  EFIAPI
  VirtioNetShutdownTx (
    IN OUT VNET_DEV *Dev
    )
  {
    ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
    TX_BUF_MAP_INFO          *TxBufMapInfo;
    VOID                     *UserStruct;
  
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->TxSharedReqMap);
    Dev->VirtIo->FreeSharedPages (
                   Dev->VirtIo,
                   EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
                   Dev->TxSharedReq
                   );
  
    for (Entry = OrderedCollectionMin (Dev->TxBufCollection);
         Entry != NULL;
         Entry = Entry2) {
      Entry2 = OrderedCollectionNext (Entry);
      OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
      TxBufMapInfo = UserStruct;
      Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);
      FreePool (TxBufMapInfo);
    }
    OrderedCollectionUninit (Dev->TxBufCollection);
  
    FreePool (Dev->TxFreeStack);
  }
  
  /**
    Release TX and RX VRING resources.
  
    @param[in,out] Dev       The VNET_DEV driver instance which was using
                             the ring.
    @param[in,out] Ring      The virtio ring to clean up.
    @param[in]     RingMap   A token return from the VirtioRingMap()
  */
  VOID
  EFIAPI
  VirtioNetUninitRing (
    IN OUT VNET_DEV *Dev,
    IN OUT VRING    *Ring,
    IN     VOID     *RingMap
    )
  {
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RingMap);
    VirtioRingUninit (Dev->VirtIo, Ring);
  }
  
  
  /**
    Map Caller-supplied TxBuf buffer to the device-mapped address
  
    @param[in]    Dev               The VNET_DEV driver instance which wants to
                                    map the Tx packet.
    @param[in]    Buffer            The system physical address of TxBuf
    @param[in]    NumberOfBytes     Number of bytes to map
    @param[out]   DeviceAddress     The resulting device address for the bus
                                    master access.
  
    @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to
                                    a lack of resources.
    @return                         Status codes from
                                    VirtioMapAllBytesInSharedBuffer()
    @retval EFI_SUCCESS             Caller-supplied buffer is successfully mapped.
  */
  EFI_STATUS
  EFIAPI
  VirtioNetMapTxBuf (
    IN  VNET_DEV              *Dev,
    IN  VOID                  *Buffer,
    IN  UINTN                 NumberOfBytes,
    OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress
    )
  {
    EFI_STATUS                Status;
    TX_BUF_MAP_INFO           *TxBufMapInfo;
    EFI_PHYSICAL_ADDRESS      Address;
    VOID                      *Mapping;
  
    TxBufMapInfo = AllocatePool (sizeof (*TxBufMapInfo));
    if (TxBufMapInfo == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
  
    Status = VirtioMapAllBytesInSharedBuffer (
               Dev->VirtIo,
               VirtioOperationBusMasterRead,
               Buffer,
               NumberOfBytes,
               &Address,
               &Mapping
              );
    if (EFI_ERROR (Status)) {
      goto FreeTxBufMapInfo;
    }
  
    TxBufMapInfo->Buffer = Buffer;
    TxBufMapInfo->DeviceAddress = Address;
    TxBufMapInfo->BufMap = Mapping;
  
    Status = OrderedCollectionInsert (
               Dev->TxBufCollection,
               NULL,
               TxBufMapInfo
               );
    switch (Status) {
    case EFI_OUT_OF_RESOURCES:
      goto UnmapTxBuf;
    case EFI_ALREADY_STARTED:
      //
      // This should never happen: it implies
      //
      // - an identity-mapping VIRTIO_DEVICE_PROTOCOL.MapSharedBuffer()
      //   implementation -- which is fine,
      //
      // - and an SNP client that queues multiple instances of the exact same
      //   buffer address with SNP.Transmit() -- which is undefined behavior,
      //   based on the TxBuf language in UEFI-2.7,
      //   EFI_SIMPLE_NETWORK.GetStatus().
      //
      ASSERT (FALSE);
      Status = EFI_INVALID_PARAMETER;
      goto UnmapTxBuf;
    default:
      ASSERT_EFI_ERROR (Status);
      break;
    }
  
    *DeviceAddress = Address;
    return EFI_SUCCESS;
  
  UnmapTxBuf:
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);
  
  FreeTxBufMapInfo:
    FreePool (TxBufMapInfo);
    return Status;
  }
  
  /**
    Unmap (aka reverse mapping) device mapped TxBuf buffer to the system
    physical address
  
    @param[in]    Dev               The VNET_DEV driver instance which wants to
                                    reverse- and unmap the Tx packet.
    @param[out]   Buffer            The system physical address of TxBuf
    @param[in]    DeviceAddress     The device address for the TxBuf
  
    @retval EFI_INVALID_PARAMETER   The DeviceAddress is not mapped
    @retval EFI_SUCCESS             The TxBuf at DeviceAddress has been unmapped,
                                    and Buffer has been set to TxBuf's system
                                    physical address.
  
  */
  EFI_STATUS
  EFIAPI
  VirtioNetUnmapTxBuf (
    IN  VNET_DEV              *Dev,
    OUT VOID                  **Buffer,
    IN  EFI_PHYSICAL_ADDRESS  DeviceAddress
    )
  {
    ORDERED_COLLECTION_ENTRY  *Entry;
    TX_BUF_MAP_INFO           *TxBufMapInfo;
    VOID                      *UserStruct;
  
    Entry = OrderedCollectionFind (Dev->TxBufCollection, &DeviceAddress);
    if (Entry == NULL) {
      return EFI_INVALID_PARAMETER;
    }
  
    OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
  
    TxBufMapInfo = UserStruct;
  
    *Buffer = TxBufMapInfo->Buffer;
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);
    FreePool (TxBufMapInfo);
  
    return EFI_SUCCESS;
  }
  
  /**
    Comparator function for two TX_BUF_MAP_INFO objects.
  
    @param[in] UserStruct1  Pointer to the first TX_BUF_MAP_INFO object.
  
    @param[in] UserStruct2  Pointer to the second TX_BUF_MAP_INFO object.
  
    @retval <0  If UserStruct1 compares less than UserStruct2.
  
    @retval  0  If UserStruct1 compares equal to UserStruct2.
  
    @retval >0  If UserStruct1 compares greater than UserStruct2.
  */
  INTN
  EFIAPI
  VirtioNetTxBufMapInfoCompare (
    IN CONST VOID *UserStruct1,
    IN CONST VOID *UserStruct2
    )
  {
    CONST TX_BUF_MAP_INFO *MapInfo1;
    CONST TX_BUF_MAP_INFO *MapInfo2;
  
    MapInfo1 = UserStruct1;
    MapInfo2 = UserStruct2;
  
    return MapInfo1->DeviceAddress < MapInfo2->DeviceAddress ? -1 :
           MapInfo1->DeviceAddress > MapInfo2->DeviceAddress ?  1 :
           0;
  }
  
  /**
    Compare a standalone DeviceAddress against a TX_BUF_MAP_INFO object
    containing an embedded DeviceAddress.
  
    @param[in] StandaloneKey  Pointer to DeviceAddress, which has type
                              EFI_PHYSICAL_ADDRESS.
  
    @param[in] UserStruct     Pointer to the TX_BUF_MAP_INFO object with the
                              embedded DeviceAddress.
  
    @retval <0  If StandaloneKey compares less than UserStruct's key.
  
    @retval  0  If StandaloneKey compares equal to UserStruct's key.
  
    @retval >0  If StandaloneKey compares greater than UserStruct's key.
  **/
  INTN
  EFIAPI
  VirtioNetTxBufDeviceAddressCompare (
    IN CONST VOID *StandaloneKey,
    IN CONST VOID *UserStruct
    )
  {
    CONST EFI_PHYSICAL_ADDRESS *DeviceAddress;
    CONST TX_BUF_MAP_INFO      *MapInfo;
  
    DeviceAddress = StandaloneKey;
    MapInfo = UserStruct;
  
    return *DeviceAddress < MapInfo->DeviceAddress ? -1 :
           *DeviceAddress > MapInfo->DeviceAddress ?  1 :
           0;
  }
  