123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- /** @file
- Implementation of the SNP.Initialize() function and its private helpers if
- any.
- Copyright (C) 2013, Red Hat, Inc.
- Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
- Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <Library/BaseLib.h>
- #include <Library/BaseMemoryLib.h>
- #include <Library/MemoryAllocationLib.h>
- #include <Library/UefiBootServicesTableLib.h>
- #include "VirtioNet.h"
- /**
- Initialize a virtio ring for a specific transfer direction of the virtio-net
- device.
- This function may only be called by VirtioNetInitialize().
- @param[in,out] Dev The VNET_DEV driver instance about to enter the
- EfiSimpleNetworkInitialized state.
- @param[in] Selector Identifies the transfer direction (virtio queue) of
- the network device.
- @param[out] Ring The virtio-ring inside the VNET_DEV structure,
- corresponding to Selector.
- @param[out] Mapping A resulting token to pass to VirtioNetUninitRing()
- @retval EFI_UNSUPPORTED The queue size reported by the virtio-net device is
- too small.
- @return Status codes from VIRTIO_CFG_WRITE(),
- VIRTIO_CFG_READ(), VirtioRingInit() and
- VirtioRingMap().
- @retval EFI_SUCCESS Ring initialized.
- */
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioNetInitRing (
- IN OUT VNET_DEV *Dev,
- IN UINT16 Selector,
- OUT VRING *Ring,
- OUT VOID **Mapping
- )
- {
- EFI_STATUS Status;
- UINT16 QueueSize;
- UINT64 RingBaseShift;
- VOID *MapInfo;
- //
- // step 4b -- allocate selected queue
- //
- Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // For each packet (RX and TX alike), we need two descriptors:
- // one for the virtio-net request header, and another one for the data
- //
- if (QueueSize < 2) {
- return EFI_UNSUPPORTED;
- }
- Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // If anything fails from here on, we must release the ring resources.
- //
- Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
- //
- // Additional steps for MMIO: align the queue appropriately, and set the
- // size. If anything fails from here on, we must unmap the ring resources.
- //
- Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
- if (EFI_ERROR (Status)) {
- goto UnmapQueue;
- }
- Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
- if (EFI_ERROR (Status)) {
- goto UnmapQueue;
- }
- //
- // step 4c -- report GPFN (guest-physical frame number) of queue
- //
- Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
- if (EFI_ERROR (Status)) {
- goto UnmapQueue;
- }
- *Mapping = MapInfo;
- return EFI_SUCCESS;
- UnmapQueue:
- Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
- ReleaseQueue:
- VirtioRingUninit (Dev->VirtIo, Ring);
- return Status;
- }
- /**
- Set up static scaffolding for the VirtioNetTransmit() and
- VirtioNetGetStatus() SNP methods.
- This function may only be called by VirtioNetInitialize().
- The structures laid out and resources configured include:
- - fully populate the TX queue with a static pattern of virtio descriptor
- chains,
- - tracking of heads of free descriptor chains from the above,
- - one common virtio-net request header (never modified by the host) for all
- pending TX packets,
- - select polling over TX interrupt.
- @param[in,out] Dev The VNET_DEV driver instance about to enter the
- EfiSimpleNetworkInitialized state.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads
- of free descriptor chains or failed to init
- TxBufCollection.
- @return Status codes from VIRTIO_DEVICE_PROTOCOL.
- AllocateSharedPages() or
- VirtioMapAllBytesInSharedBuffer()
- @retval EFI_SUCCESS TX setup successful.
- */
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioNetInitTx (
- IN OUT VNET_DEV *Dev
- )
- {
- UINTN TxSharedReqSize;
- UINTN PktIdx;
- EFI_STATUS Status;
- EFI_PHYSICAL_ADDRESS DeviceAddress;
- VOID *TxSharedReqBuffer;
- Dev->TxMaxPending = (UINT16)MIN (
- Dev->TxRing.QueueSize / 2,
- VNET_MAX_PENDING
- );
- Dev->TxCurPending = 0;
- Dev->TxFreeStack = AllocatePool (
- Dev->TxMaxPending *
- sizeof *Dev->TxFreeStack
- );
- if (Dev->TxFreeStack == NULL) {
- 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.
- //
- Status = Dev->VirtIo->AllocateSharedPages (
- Dev->VirtIo,
- EFI_SIZE_TO_PAGES (sizeof *Dev->TxSharedReq),
- &TxSharedReqBuffer
- );
- if (EFI_ERROR (Status)) {
- goto UninitTxBufCollection;
- }
- ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);
- Status = VirtioMapAllBytesInSharedBuffer (
- Dev->VirtIo,
- VirtioOperationBusMasterCommonBuffer,
- TxSharedReqBuffer,
- sizeof *(Dev->TxSharedReq),
- &DeviceAddress,
- &Dev->TxSharedReqMap
- );
- if (EFI_ERROR (Status)) {
- goto FreeTxSharedReqBuffer;
- }
- Dev->TxSharedReq = TxSharedReqBuffer;
- //
- // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
- // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
- //
- TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
- sizeof (Dev->TxSharedReq->V0_9_5) :
- sizeof *Dev->TxSharedReq;
- for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
- UINT16 DescIdx;
- DescIdx = (UINT16)(2 * PktIdx);
- Dev->TxFreeStack[PktIdx] = DescIdx;
- //
- // For each possibly pending packet, lay out the descriptor for the common
- // (unmodified by the host) virtio-net request header.
- //
- Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;
- Dev->TxRing.Desc[DescIdx].Len = (UINT32)TxSharedReqSize;
- Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
- Dev->TxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);
- //
- // The second descriptor of each pending TX packet is updated on the fly,
- // but it always terminates the descriptor chain of the packet.
- //
- Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
- }
- //
- // virtio-0.9.5, Appendix C, Packet Transmission
- //
- Dev->TxSharedReq->V0_9_5.Flags = 0;
- Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
- //
- // For VirtIo 1.0 only -- the field exists, but it is unused
- //
- Dev->TxSharedReq->NumBuffers = 0;
- //
- // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
- //
- MemoryFence ();
- Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
- ASSERT (Dev->TxLastUsed == 0);
- //
- // want no interrupt when a transmit completes
- //
- *Dev->TxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;
- return EFI_SUCCESS;
- FreeTxSharedReqBuffer:
- Dev->VirtIo->FreeSharedPages (
- Dev->VirtIo,
- EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
- TxSharedReqBuffer
- );
- UninitTxBufCollection:
- OrderedCollectionUninit (Dev->TxBufCollection);
- FreeTxFreeStack:
- FreePool (Dev->TxFreeStack);
- return Status;
- }
- /**
- Set up static scaffolding for the VirtioNetReceive() SNP method and enable
- live device operation.
- This function may only be called as VirtioNetInitialize()'s final step.
- The structures laid out and resources configured include:
- - destination area for the host to write virtio-net request headers and
- packet data into,
- - select polling over RX interrupt,
- - fully populate the RX queue with a static pattern of virtio descriptor
- chains.
- @param[in,out] Dev The VNET_DEV driver instance about to enter the
- EfiSimpleNetworkInitialized state.
- @return Status codes from VIRTIO_CFG_WRITE() or
- VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
- VirtioMapAllBytesInSharedBuffer().
- @retval EFI_SUCCESS RX setup successful. The device is live and may
- already be writing to the receive area.
- */
- STATIC
- EFI_STATUS
- EFIAPI
- VirtioNetInitRx (
- IN OUT VNET_DEV *Dev
- )
- {
- EFI_STATUS Status;
- UINTN VirtioNetReqSize;
- UINTN RxBufSize;
- UINT16 RxAlwaysPending;
- UINTN PktIdx;
- UINT16 DescIdx;
- UINTN NumBytes;
- EFI_PHYSICAL_ADDRESS RxBufDeviceAddress;
- VOID *RxBuffer;
- //
- // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
- // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
- //
- VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
- sizeof (VIRTIO_NET_REQ) :
- sizeof (VIRTIO_1_0_NET_REQ);
- //
- // For each incoming packet we must supply two descriptors:
- // - the recipient for the virtio-net request header, plus
- // - the recipient for the network data (which consists of Ethernet header
- // and Ethernet payload).
- //
- RxBufSize = VirtioNetReqSize +
- (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
- //
- // Limit the number of pending RX packets if the queue is big. The division
- // by two is due to the above "two descriptors per packet" trait.
- //
- RxAlwaysPending = (UINT16)MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
- //
- // The RxBuf is shared between guest and hypervisor, use
- // AllocateSharedPages() to allocate this memory region and map it with
- // BusMasterCommonBuffer so that it can be accessed by both guest and
- // hypervisor.
- //
- NumBytes = RxAlwaysPending * RxBufSize;
- Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);
- Status = Dev->VirtIo->AllocateSharedPages (
- Dev->VirtIo,
- Dev->RxBufNrPages,
- &RxBuffer
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- ZeroMem (RxBuffer, NumBytes);
- Status = VirtioMapAllBytesInSharedBuffer (
- Dev->VirtIo,
- VirtioOperationBusMasterCommonBuffer,
- RxBuffer,
- NumBytes,
- &Dev->RxBufDeviceBase,
- &Dev->RxBufMap
- );
- if (EFI_ERROR (Status)) {
- goto FreeSharedBuffer;
- }
- Dev->RxBuf = RxBuffer;
- //
- // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
- //
- MemoryFence ();
- Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
- ASSERT (Dev->RxLastUsed == 0);
- //
- // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
- // the host should not send interrupts, we'll poll in VirtioNetReceive()
- // and VirtioNetIsPacketAvailable().
- //
- *Dev->RxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;
- //
- // now set up a separate, two-part descriptor chain for each RX packet, and
- // link each chain into (from) the available ring as well
- //
- DescIdx = 0;
- RxBufDeviceAddress = Dev->RxBufDeviceBase;
- for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
- //
- // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
- // invisible to the host until we update the Index Field
- //
- Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
- //
- // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
- //
- Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;
- Dev->RxRing.Desc[DescIdx].Len = (UINT32)VirtioNetReqSize;
- Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
- Dev->RxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);
- RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
- Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;
- Dev->RxRing.Desc[DescIdx].Len = (UINT32)(RxBufSize - VirtioNetReqSize);
- Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;
- RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
- }
- //
- // virtio-0.9.5, 2.4.1.3 Updating the Index Field
- //
- MemoryFence ();
- *Dev->RxRing.Avail.Idx = RxAlwaysPending;
- //
- // At this point reception may already be running. In order to make it sure,
- // kick the hypervisor. If we fail to kick it, we must first abort reception
- // before tearing down anything, because reception may have been already
- // running even without the kick.
- //
- // virtio-0.9.5, 2.4.1.4 Notifying the Device
- //
- MemoryFence ();
- Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
- if (EFI_ERROR (Status)) {
- Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
- goto UnmapSharedBuffer;
- }
- return Status;
- UnmapSharedBuffer:
- Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
- FreeSharedBuffer:
- Dev->VirtIo->FreeSharedPages (
- Dev->VirtIo,
- Dev->RxBufNrPages,
- RxBuffer
- );
- return Status;
- }
- /**
- Resets a network adapter and allocates the transmit and receive buffers
- required by the network interface; optionally, also requests allocation of
- additional transmit and receive buffers.
- @param This The protocol instance pointer.
- @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer
- space that the driver should allocate for the
- network interface. Some network interfaces will not
- be able to use the extra buffer, and the caller
- will not know if it is actually being used.
- @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
- space that the driver should allocate for the
- network interface. Some network interfaces will not
- be able to use the extra buffer, and the caller
- will not know if it is actually being used.
- @retval EFI_SUCCESS The network interface was initialized.
- @retval EFI_NOT_STARTED The network interface has not been started.
- @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit
- and receive buffers.
- @retval EFI_INVALID_PARAMETER One or more of the parameters has an
- unsupported value.
- @retval EFI_DEVICE_ERROR The command could not be sent to the network
- interface.
- @retval EFI_UNSUPPORTED This function is not supported by the network
- interface.
- **/
- EFI_STATUS
- EFIAPI
- VirtioNetInitialize (
- IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
- IN UINTN ExtraRxBufferSize OPTIONAL,
- IN UINTN ExtraTxBufferSize OPTIONAL
- )
- {
- VNET_DEV *Dev;
- EFI_TPL OldTpl;
- EFI_STATUS Status;
- UINT8 NextDevStat;
- UINT64 Features;
- if (This == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- if ((ExtraRxBufferSize > 0) || (ExtraTxBufferSize > 0)) {
- return EFI_UNSUPPORTED;
- }
- Dev = VIRTIO_NET_FROM_SNP (This);
- OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
- if (Dev->Snm.State != EfiSimpleNetworkStarted) {
- Status = EFI_NOT_STARTED;
- goto InitFailed;
- }
- //
- // In the EfiSimpleNetworkStarted state the virtio-net device has status
- // value 0 (= reset) -- see the state diagram, the full call chain to
- // the end of VirtioNetGetFeatures() (considering we're here now),
- // the DeviceFailed label below, and VirtioNetShutdown().
- //
- // Accordingly, the below is a subsequence of the steps found in the
- // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
- //
- NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto InitFailed;
- }
- NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto DeviceFailed;
- }
- //
- // Set Page Size - MMIO VirtIo Specific
- //
- Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
- if (EFI_ERROR (Status)) {
- goto DeviceFailed;
- }
- //
- // step 4a -- retrieve features. Note that we're past validating required
- // features in VirtioNetGetFeatures().
- //
- Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
- if (EFI_ERROR (Status)) {
- goto DeviceFailed;
- }
- ASSERT (Features & VIRTIO_NET_F_MAC);
- ASSERT (
- Dev->Snm.MediaPresentSupported ==
- !!(Features & VIRTIO_NET_F_STATUS)
- );
- Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |
- VIRTIO_F_IOMMU_PLATFORM;
- //
- // In virtio-1.0, feature negotiation is expected to complete before queue
- // discovery, and the device can also reject the selected set of features.
- //
- if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
- Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
- if (EFI_ERROR (Status)) {
- goto DeviceFailed;
- }
- }
- //
- // step 4b, 4c -- allocate and report virtqueues
- //
- Status = VirtioNetInitRing (
- Dev,
- VIRTIO_NET_Q_RX,
- &Dev->RxRing,
- &Dev->RxRingMap
- );
- if (EFI_ERROR (Status)) {
- goto DeviceFailed;
- }
- Status = VirtioNetInitRing (
- Dev,
- VIRTIO_NET_Q_TX,
- &Dev->TxRing,
- &Dev->TxRingMap
- );
- if (EFI_ERROR (Status)) {
- goto ReleaseRxRing;
- }
- //
- // step 5 -- keep only the features we want
- //
- if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
- Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
- Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
- if (EFI_ERROR (Status)) {
- goto ReleaseTxRing;
- }
- }
- //
- // step 6 -- virtio-net initialization complete
- //
- NextDevStat |= VSTAT_DRIVER_OK;
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto ReleaseTxRing;
- }
- Status = VirtioNetInitTx (Dev);
- if (EFI_ERROR (Status)) {
- goto AbortDevice;
- }
- //
- // start receiving
- //
- Status = VirtioNetInitRx (Dev);
- if (EFI_ERROR (Status)) {
- goto ReleaseTxAux;
- }
- Dev->Snm.State = EfiSimpleNetworkInitialized;
- gBS->RestoreTPL (OldTpl);
- return EFI_SUCCESS;
- ReleaseTxAux:
- VirtioNetShutdownTx (Dev);
- AbortDevice:
- Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
- ReleaseTxRing:
- VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
- ReleaseRxRing:
- VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
- DeviceFailed:
- //
- // restore device status invariant for the EfiSimpleNetworkStarted state
- //
- Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
- InitFailed:
- gBS->RestoreTPL (OldTpl);
- return Status;
- }
|