audk/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c

303 lines
8.6 KiB
C
Raw Normal View History

/** @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;
OvmfPkg/VirtioNetDxe: dynamically alloc transmit header Each network packet is submitted for transmission by pushing the head descriptor of a two-part descriptor chain to the Available Ring of the TX queue. VirtioNetInitTx() sets up the the descriptor chains for all queueable packets in advance, and points all the head descriptors to the same shared, never modified, VIRTIO_1_0_NET_REQ header object (or its initial VIRTIO_NET_REQ sub-object, dependent on virtio version). VirtioNetInitTx() currently uses the header object's system physical address for populating the head descriptors. When device is behind the IOMMU, VirtioNet driver is required to provide the device address of VIRTIO_1_0_NET_REQ header. In this patch we dynamically allocate the header using AllocateSharedPages() and map with BusMasterCommonBuffer so that header can be accessed by both processor and the device. We map the header object for CommonBuffer operation because, in order to stick with the current code order, we populate the head descriptors with the header's device address first, and fill in the header itself second. 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>
2017-09-14 23:22:43 +02:00
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;
}