diff --git a/OvmfPkg/VirtioNetDxe/SnpInitialize.c b/OvmfPkg/VirtioNetDxe/SnpInitialize.c index 9621f936d2..ffb3deefe0 100644 --- a/OvmfPkg/VirtioNetDxe/SnpInitialize.c +++ b/OvmfPkg/VirtioNetDxe/SnpInitialize.c @@ -147,7 +147,8 @@ ReleaseQueue: EfiSimpleNetworkInitialized state. @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads - of free descriptor chains. + of free descriptor chains or failed to init + TxBufCollection. @return Status codes from VIRTIO_DEVICE_PROTOCOL. AllocateSharedPages() or VirtioMapAllBytesInSharedBuffer() @@ -176,6 +177,15 @@ VirtioNetInitTx ( return EFI_OUT_OF_RESOURCES; } + Dev->TxBufCollection = OrderedCollectionInit ( + VirtioNetTxBufMapInfoCompare, + VirtioNetTxBufDeviceAddressCompare + ); + if (Dev->TxBufCollection == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeTxFreeStack; + } + // // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it // can be accessed equally by both processor and device. @@ -186,7 +196,7 @@ VirtioNetInitTx ( &TxSharedReqBuffer ); if (EFI_ERROR (Status)) { - goto FreeTxFreeStack; + goto UninitTxBufCollection; } ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq); @@ -267,6 +277,10 @@ FreeTxSharedReqBuffer: EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)), TxSharedReqBuffer ); + +UninitTxBufCollection: + OrderedCollectionUninit (Dev->TxBufCollection); + FreeTxFreeStack: FreePool (Dev->TxFreeStack); diff --git a/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c b/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c index 2fce8142d5..18dbf18125 100644 --- a/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c +++ b/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c @@ -18,6 +18,16 @@ #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. @@ -54,6 +64,10 @@ 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, @@ -61,6 +75,17 @@ VirtioNetShutdownTx ( 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); } @@ -83,3 +108,202 @@ VirtioNetUninitRing ( 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 succesfully 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; +} diff --git a/OvmfPkg/VirtioNetDxe/VirtioNet.h b/OvmfPkg/VirtioNetDxe/VirtioNet.h index 027f75993e..3fc88cfb79 100644 --- a/OvmfPkg/VirtioNetDxe/VirtioNet.h +++ b/OvmfPkg/VirtioNetDxe/VirtioNet.h @@ -26,6 +26,7 @@ #include #include #include +#include #define VNET_SIG SIGNATURE_32 ('V', 'N', 'E', 'T') @@ -100,6 +101,7 @@ typedef struct { VIRTIO_1_0_NET_REQ *TxSharedReq; // VirtioNetInitTx VOID *TxSharedReqMap; // VirtioNetInitTx UINT16 TxLastUsed; // VirtioNetInitTx + ORDERED_COLLECTION *TxBufCollection; // VirtioNetInitTx } VNET_DEV; @@ -280,6 +282,42 @@ VirtioNetUninitRing ( IN VOID *RingMap ); +// +// utility functions to map caller-supplied Tx buffer system physical address +// to a device address and vice versa +// +EFI_STATUS +EFIAPI +VirtioNetMapTxBuf ( + IN VNET_DEV *Dev, + IN VOID *Buffer, + IN UINTN NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress + ); + +EFI_STATUS +EFIAPI +VirtioNetUnmapTxBuf ( + IN VNET_DEV *Dev, + OUT VOID **Buffer, + IN EFI_PHYSICAL_ADDRESS DeviceAddress + ); + +INTN +EFIAPI +VirtioNetTxBufMapInfoCompare ( + IN CONST VOID *UserStruct1, + IN CONST VOID *UserStruct2 + ); + +INTN +EFIAPI +VirtioNetTxBufDeviceAddressCompare ( + IN CONST VOID *StandaloneKey, + IN CONST VOID *UserStruct + ); + + // // event callbacks // diff --git a/OvmfPkg/VirtioNetDxe/VirtioNet.inf b/OvmfPkg/VirtioNetDxe/VirtioNet.inf index a855ad4ac1..9ff6d87e61 100644 --- a/OvmfPkg/VirtioNetDxe/VirtioNet.inf +++ b/OvmfPkg/VirtioNetDxe/VirtioNet.inf @@ -49,6 +49,7 @@ DebugLib DevicePathLib MemoryAllocationLib + OrderedCollectionLib UefiBootServicesTableLib UefiDriverEntryPoint UefiLib