/** @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 #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; }