| /** @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> | |
| This program and the accompanying materials are licensed and made available | |
| under the terms and conditions of the BSD License which accompanies this | |
| distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT | |
| WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #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; | |
| } |