1 /** @file
2 
3   Implementation of the SNP.Initialize() function and its private helpers if
4   any.
5 
6   Copyright (C) 2013, Red Hat, Inc.
7   Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
8   Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
9 
10   SPDX-License-Identifier: BSD-2-Clause-Patent
11 
12 **/
13 
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 
19 #include "VirtioNet.h"
20 
21 /**
22   Initialize a virtio ring for a specific transfer direction of the virtio-net
23   device.
24 
25   This function may only be called by VirtioNetInitialize().
26 
27   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
28                            EfiSimpleNetworkInitialized state.
29   @param[in]     Selector  Identifies the transfer direction (virtio queue) of
30                            the network device.
31   @param[out]    Ring      The virtio-ring inside the VNET_DEV structure,
32                            corresponding to Selector.
33   @param[out]    Mapping   A resulting token to pass to VirtioNetUninitRing()
34 
35   @retval EFI_UNSUPPORTED  The queue size reported by the virtio-net device is
36                            too small.
37   @return                  Status codes from VIRTIO_CFG_WRITE(),
38                            VIRTIO_CFG_READ(), VirtioRingInit() and
39                            VirtioRingMap().
40   @retval EFI_SUCCESS      Ring initialized.
41 */
42 
43 STATIC
44 EFI_STATUS
45 EFIAPI
VirtioNetInitRing(IN OUT VNET_DEV * Dev,IN UINT16 Selector,OUT VRING * Ring,OUT VOID ** Mapping)46 VirtioNetInitRing (
47   IN OUT VNET_DEV *Dev,
48   IN     UINT16   Selector,
49   OUT    VRING    *Ring,
50   OUT    VOID     **Mapping
51   )
52 {
53   EFI_STATUS Status;
54   UINT16     QueueSize;
55   UINT64     RingBaseShift;
56   VOID       *MapInfo;
57 
58   //
59   // step 4b -- allocate selected queue
60   //
61   Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
62   if (EFI_ERROR (Status)) {
63     return Status;
64   }
65   Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
66   if (EFI_ERROR (Status)) {
67     return Status;
68   }
69 
70   //
71   // For each packet (RX and TX alike), we need two descriptors:
72   // one for the virtio-net request header, and another one for the data
73   //
74   if (QueueSize < 2) {
75     return EFI_UNSUPPORTED;
76   }
77   Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
78   if (EFI_ERROR (Status)) {
79     return Status;
80   }
81 
82   //
83   // If anything fails from here on, we must release the ring resources.
84   //
85   Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
86   if (EFI_ERROR (Status)) {
87     goto ReleaseQueue;
88   }
89 
90   //
91   // Additional steps for MMIO: align the queue appropriately, and set the
92   // size. If anything fails from here on, we must unmap the ring resources.
93   //
94   Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
95   if (EFI_ERROR (Status)) {
96     goto UnmapQueue;
97   }
98 
99   Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
100   if (EFI_ERROR (Status)) {
101     goto UnmapQueue;
102   }
103 
104   //
105   // step 4c -- report GPFN (guest-physical frame number) of queue
106   //
107   Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
108   if (EFI_ERROR (Status)) {
109     goto UnmapQueue;
110   }
111 
112   *Mapping = MapInfo;
113 
114   return EFI_SUCCESS;
115 
116 UnmapQueue:
117   Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
118 
119 ReleaseQueue:
120   VirtioRingUninit (Dev->VirtIo, Ring);
121 
122   return Status;
123 }
124 
125 
126 /**
127   Set up static scaffolding for the VirtioNetTransmit() and
128   VirtioNetGetStatus() SNP methods.
129 
130   This function may only be called by VirtioNetInitialize().
131 
132   The structures laid out and resources configured include:
133   - fully populate the TX queue with a static pattern of virtio descriptor
134     chains,
135   - tracking of heads of free descriptor chains from the above,
136   - one common virtio-net request header (never modified by the host) for all
137     pending TX packets,
138   - select polling over TX interrupt.
139 
140   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
141                            EfiSimpleNetworkInitialized state.
142 
143   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the stack to track the heads
144                                 of free descriptor chains or failed to init
145                                 TxBufCollection.
146   @return                       Status codes from VIRTIO_DEVICE_PROTOCOL.
147                                 AllocateSharedPages() or
148                                 VirtioMapAllBytesInSharedBuffer()
149   @retval EFI_SUCCESS           TX setup successful.
150 */
151 
152 STATIC
153 EFI_STATUS
154 EFIAPI
VirtioNetInitTx(IN OUT VNET_DEV * Dev)155 VirtioNetInitTx (
156   IN OUT VNET_DEV *Dev
157   )
158 {
159   UINTN                 TxSharedReqSize;
160   UINTN                 PktIdx;
161   EFI_STATUS            Status;
162   EFI_PHYSICAL_ADDRESS  DeviceAddress;
163   VOID                  *TxSharedReqBuffer;
164 
165   Dev->TxMaxPending = (UINT16) MIN (Dev->TxRing.QueueSize / 2,
166                                  VNET_MAX_PENDING);
167   Dev->TxCurPending = 0;
168   Dev->TxFreeStack  = AllocatePool (Dev->TxMaxPending *
169                         sizeof *Dev->TxFreeStack);
170   if (Dev->TxFreeStack == NULL) {
171     return EFI_OUT_OF_RESOURCES;
172   }
173 
174   Dev->TxBufCollection = OrderedCollectionInit (
175                            VirtioNetTxBufMapInfoCompare,
176                            VirtioNetTxBufDeviceAddressCompare
177                            );
178   if (Dev->TxBufCollection == NULL) {
179     Status = EFI_OUT_OF_RESOURCES;
180     goto FreeTxFreeStack;
181   }
182 
183   //
184   // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it
185   // can be accessed equally by both processor and device.
186   //
187   Status = Dev->VirtIo->AllocateSharedPages (
188                           Dev->VirtIo,
189                           EFI_SIZE_TO_PAGES (sizeof *Dev->TxSharedReq),
190                           &TxSharedReqBuffer
191                           );
192   if (EFI_ERROR (Status)) {
193     goto UninitTxBufCollection;
194   }
195 
196   ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);
197 
198   Status = VirtioMapAllBytesInSharedBuffer (
199              Dev->VirtIo,
200              VirtioOperationBusMasterCommonBuffer,
201              TxSharedReqBuffer,
202              sizeof *(Dev->TxSharedReq),
203              &DeviceAddress,
204              &Dev->TxSharedReqMap
205              );
206   if (EFI_ERROR (Status)) {
207     goto FreeTxSharedReqBuffer;
208   }
209 
210   Dev->TxSharedReq = TxSharedReqBuffer;
211 
212 
213   //
214   // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
215   // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
216   //
217   TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
218                     sizeof (Dev->TxSharedReq->V0_9_5) :
219                     sizeof *Dev->TxSharedReq;
220 
221   for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
222     UINT16 DescIdx;
223 
224     DescIdx = (UINT16) (2 * PktIdx);
225     Dev->TxFreeStack[PktIdx] = DescIdx;
226 
227     //
228     // For each possibly pending packet, lay out the descriptor for the common
229     // (unmodified by the host) virtio-net request header.
230     //
231     Dev->TxRing.Desc[DescIdx].Addr  = DeviceAddress;
232     Dev->TxRing.Desc[DescIdx].Len   = (UINT32) TxSharedReqSize;
233     Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
234     Dev->TxRing.Desc[DescIdx].Next  = (UINT16) (DescIdx + 1);
235 
236     //
237     // The second descriptor of each pending TX packet is updated on the fly,
238     // but it always terminates the descriptor chain of the packet.
239     //
240     Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
241   }
242 
243   //
244   // virtio-0.9.5, Appendix C, Packet Transmission
245   //
246   Dev->TxSharedReq->V0_9_5.Flags   = 0;
247   Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
248 
249   //
250   // For VirtIo 1.0 only -- the field exists, but it is unused
251   //
252   Dev->TxSharedReq->NumBuffers = 0;
253 
254   //
255   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
256   //
257   MemoryFence ();
258   Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
259   ASSERT (Dev->TxLastUsed == 0);
260 
261   //
262   // want no interrupt when a transmit completes
263   //
264   *Dev->TxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
265 
266   return EFI_SUCCESS;
267 
268 FreeTxSharedReqBuffer:
269   Dev->VirtIo->FreeSharedPages (
270                  Dev->VirtIo,
271                  EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
272                  TxSharedReqBuffer
273                  );
274 
275 UninitTxBufCollection:
276   OrderedCollectionUninit (Dev->TxBufCollection);
277 
278 FreeTxFreeStack:
279   FreePool (Dev->TxFreeStack);
280 
281   return Status;
282 }
283 
284 
285 /**
286   Set up static scaffolding for the VirtioNetReceive() SNP method and enable
287   live device operation.
288 
289   This function may only be called as VirtioNetInitialize()'s final step.
290 
291   The structures laid out and resources configured include:
292   - destination area for the host to write virtio-net request headers and
293     packet data into,
294   - select polling over RX interrupt,
295   - fully populate the RX queue with a static pattern of virtio descriptor
296     chains.
297 
298   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
299                            EfiSimpleNetworkInitialized state.
300 
301   @return                       Status codes from VIRTIO_CFG_WRITE() or
302                                 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
303                                 VirtioMapAllBytesInSharedBuffer().
304   @retval EFI_SUCCESS           RX setup successful. The device is live and may
305                                 already be writing to the receive area.
306 */
307 
308 STATIC
309 EFI_STATUS
310 EFIAPI
VirtioNetInitRx(IN OUT VNET_DEV * Dev)311 VirtioNetInitRx (
312   IN OUT VNET_DEV *Dev
313   )
314 {
315   EFI_STATUS            Status;
316   UINTN                 VirtioNetReqSize;
317   UINTN                 RxBufSize;
318   UINT16                RxAlwaysPending;
319   UINTN                 PktIdx;
320   UINT16                DescIdx;
321   UINTN                 NumBytes;
322   EFI_PHYSICAL_ADDRESS  RxBufDeviceAddress;
323   VOID                  *RxBuffer;
324 
325   //
326   // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
327   // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
328   //
329   VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
330                      sizeof (VIRTIO_NET_REQ) :
331                      sizeof (VIRTIO_1_0_NET_REQ);
332 
333   //
334   // For each incoming packet we must supply two descriptors:
335   // - the recipient for the virtio-net request header, plus
336   // - the recipient for the network data (which consists of Ethernet header
337   //   and Ethernet payload).
338   //
339   RxBufSize = VirtioNetReqSize +
340               (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
341 
342   //
343   // Limit the number of pending RX packets if the queue is big. The division
344   // by two is due to the above "two descriptors per packet" trait.
345   //
346   RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
347 
348   //
349   // The RxBuf is shared between guest and hypervisor, use
350   // AllocateSharedPages() to allocate this memory region and map it with
351   // BusMasterCommonBuffer so that it can be accessed by both guest and
352   // hypervisor.
353   //
354   NumBytes = RxAlwaysPending * RxBufSize;
355   Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);
356   Status = Dev->VirtIo->AllocateSharedPages (
357                           Dev->VirtIo,
358                           Dev->RxBufNrPages,
359                           &RxBuffer
360                           );
361   if (EFI_ERROR (Status)) {
362     return Status;
363   }
364 
365   ZeroMem (RxBuffer, NumBytes);
366 
367   Status = VirtioMapAllBytesInSharedBuffer (
368              Dev->VirtIo,
369              VirtioOperationBusMasterCommonBuffer,
370              RxBuffer,
371              NumBytes,
372              &Dev->RxBufDeviceBase,
373              &Dev->RxBufMap
374              );
375   if (EFI_ERROR (Status)) {
376     goto FreeSharedBuffer;
377   }
378 
379   Dev->RxBuf = RxBuffer;
380 
381   //
382   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
383   //
384   MemoryFence ();
385   Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
386   ASSERT (Dev->RxLastUsed == 0);
387 
388   //
389   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
390   // the host should not send interrupts, we'll poll in VirtioNetReceive()
391   // and VirtioNetIsPacketAvailable().
392   //
393   *Dev->RxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
394 
395   //
396   // now set up a separate, two-part descriptor chain for each RX packet, and
397   // link each chain into (from) the available ring as well
398   //
399   DescIdx = 0;
400   RxBufDeviceAddress = Dev->RxBufDeviceBase;
401   for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
402     //
403     // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
404     // invisible to the host until we update the Index Field
405     //
406     Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
407 
408     //
409     // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
410     //
411     Dev->RxRing.Desc[DescIdx].Addr  = RxBufDeviceAddress;
412     Dev->RxRing.Desc[DescIdx].Len   = (UINT32) VirtioNetReqSize;
413     Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
414     Dev->RxRing.Desc[DescIdx].Next  = (UINT16) (DescIdx + 1);
415     RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
416 
417     Dev->RxRing.Desc[DescIdx].Addr  = RxBufDeviceAddress;
418     Dev->RxRing.Desc[DescIdx].Len   = (UINT32) (RxBufSize - VirtioNetReqSize);
419     Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;
420     RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;
421   }
422 
423   //
424   // virtio-0.9.5, 2.4.1.3 Updating the Index Field
425   //
426   MemoryFence ();
427   *Dev->RxRing.Avail.Idx = RxAlwaysPending;
428 
429   //
430   // At this point reception may already be running. In order to make it sure,
431   // kick the hypervisor. If we fail to kick it, we must first abort reception
432   // before tearing down anything, because reception may have been already
433   // running even without the kick.
434   //
435   // virtio-0.9.5, 2.4.1.4 Notifying the Device
436   //
437   MemoryFence ();
438   Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
439   if (EFI_ERROR (Status)) {
440     Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
441     goto UnmapSharedBuffer;
442   }
443 
444   return Status;
445 
446 UnmapSharedBuffer:
447   Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
448 
449 FreeSharedBuffer:
450   Dev->VirtIo->FreeSharedPages (
451                  Dev->VirtIo,
452                  Dev->RxBufNrPages,
453                  RxBuffer
454                  );
455   return Status;
456 }
457 
458 
459 /**
460   Resets a network adapter and allocates the transmit and receive buffers
461   required by the network interface; optionally, also requests allocation  of
462   additional transmit and receive buffers.
463 
464   @param  This              The protocol instance pointer.
465   @param  ExtraRxBufferSize The size, in bytes, of the extra receive buffer
466                             space that the driver should allocate for the
467                             network interface. Some network interfaces will not
468                             be able to use the extra buffer, and the caller
469                             will not know if it is actually being used.
470   @param  ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
471                             space that the driver should allocate for the
472                             network interface. Some network interfaces will not
473                             be able to use the extra buffer, and the caller
474                             will not know if it is actually being used.
475 
476   @retval EFI_SUCCESS           The network interface was initialized.
477   @retval EFI_NOT_STARTED       The network interface has not been started.
478   @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit
479                                 and receive buffers.
480   @retval EFI_INVALID_PARAMETER One or more of the parameters has an
481                                 unsupported value.
482   @retval EFI_DEVICE_ERROR      The command could not be sent to the network
483                                 interface.
484   @retval EFI_UNSUPPORTED       This function is not supported by the network
485                                 interface.
486 
487 **/
488 
489 EFI_STATUS
490 EFIAPI
VirtioNetInitialize(IN EFI_SIMPLE_NETWORK_PROTOCOL * This,IN UINTN ExtraRxBufferSize OPTIONAL,IN UINTN ExtraTxBufferSize OPTIONAL)491 VirtioNetInitialize (
492   IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
493   IN UINTN                       ExtraRxBufferSize  OPTIONAL,
494   IN UINTN                       ExtraTxBufferSize  OPTIONAL
495   )
496 {
497   VNET_DEV   *Dev;
498   EFI_TPL    OldTpl;
499   EFI_STATUS Status;
500   UINT8      NextDevStat;
501   UINT64     Features;
502 
503   if (This == NULL) {
504     return EFI_INVALID_PARAMETER;
505   }
506   if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
507     return EFI_UNSUPPORTED;
508   }
509 
510   Dev = VIRTIO_NET_FROM_SNP (This);
511   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
512   if (Dev->Snm.State != EfiSimpleNetworkStarted) {
513     Status = EFI_NOT_STARTED;
514     goto InitFailed;
515   }
516 
517   //
518   // In the EfiSimpleNetworkStarted state the virtio-net device has status
519   // value 0 (= reset) -- see the state diagram, the full call chain to
520   // the end of VirtioNetGetFeatures() (considering we're here now),
521   // the DeviceFailed label below, and VirtioNetShutdown().
522   //
523   // Accordingly, the below is a subsequence of the steps found in the
524   // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
525   //
526   NextDevStat = VSTAT_ACK;    // step 2 -- acknowledge device presence
527   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
528   if (EFI_ERROR (Status)) {
529     goto InitFailed;
530   }
531 
532   NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
533   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
534   if (EFI_ERROR (Status)) {
535     goto DeviceFailed;
536   }
537 
538   //
539   // Set Page Size - MMIO VirtIo Specific
540   //
541   Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
542   if (EFI_ERROR (Status)) {
543     goto DeviceFailed;
544   }
545 
546   //
547   // step 4a -- retrieve features. Note that we're past validating required
548   // features in VirtioNetGetFeatures().
549   //
550   Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
551   if (EFI_ERROR (Status)) {
552     goto DeviceFailed;
553   }
554 
555   ASSERT (Features & VIRTIO_NET_F_MAC);
556   ASSERT (Dev->Snm.MediaPresentSupported ==
557     !!(Features & VIRTIO_NET_F_STATUS));
558 
559   Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |
560               VIRTIO_F_IOMMU_PLATFORM;
561 
562   //
563   // In virtio-1.0, feature negotiation is expected to complete before queue
564   // discovery, and the device can also reject the selected set of features.
565   //
566   if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
567     Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
568     if (EFI_ERROR (Status)) {
569       goto DeviceFailed;
570     }
571   }
572 
573   //
574   // step 4b, 4c -- allocate and report virtqueues
575   //
576   Status = VirtioNetInitRing (
577              Dev,
578              VIRTIO_NET_Q_RX,
579              &Dev->RxRing,
580              &Dev->RxRingMap
581              );
582   if (EFI_ERROR (Status)) {
583     goto DeviceFailed;
584   }
585 
586   Status = VirtioNetInitRing (
587              Dev,
588              VIRTIO_NET_Q_TX,
589              &Dev->TxRing,
590              &Dev->TxRingMap
591              );
592   if (EFI_ERROR (Status)) {
593     goto ReleaseRxRing;
594   }
595 
596   //
597   // step 5 -- keep only the features we want
598   //
599   if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
600     Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
601     Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
602     if (EFI_ERROR (Status)) {
603       goto ReleaseTxRing;
604     }
605   }
606 
607   //
608   // step 6 -- virtio-net initialization complete
609   //
610   NextDevStat |= VSTAT_DRIVER_OK;
611   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
612   if (EFI_ERROR (Status)) {
613     goto ReleaseTxRing;
614   }
615 
616   Status = VirtioNetInitTx (Dev);
617   if (EFI_ERROR (Status)) {
618     goto AbortDevice;
619   }
620 
621   //
622   // start receiving
623   //
624   Status = VirtioNetInitRx (Dev);
625   if (EFI_ERROR (Status)) {
626     goto ReleaseTxAux;
627   }
628 
629   Dev->Snm.State = EfiSimpleNetworkInitialized;
630   gBS->RestoreTPL (OldTpl);
631   return EFI_SUCCESS;
632 
633 ReleaseTxAux:
634   VirtioNetShutdownTx (Dev);
635 
636 AbortDevice:
637   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
638 
639 ReleaseTxRing:
640   VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
641 
642 ReleaseRxRing:
643   VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
644 
645 DeviceFailed:
646   //
647   // restore device status invariant for the EfiSimpleNetworkStarted state
648   //
649   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
650 
651 InitFailed:
652   gBS->RestoreTPL (OldTpl);
653   return Status;
654 }
655