]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / VirtioNetDxe / SnpInitialize.c
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 STATIC
43 EFI_STATUS
44 EFIAPI
45 VirtioNetInitRing (
46 IN OUT VNET_DEV *Dev,
47 IN UINT16 Selector,
48 OUT VRING *Ring,
49 OUT VOID **Mapping
50 )
51 {
52 EFI_STATUS Status;
53 UINT16 QueueSize;
54 UINT64 RingBaseShift;
55 VOID *MapInfo;
56
57 //
58 // step 4b -- allocate selected queue
59 //
60 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
61 if (EFI_ERROR (Status)) {
62 return Status;
63 }
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
78 Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
79 if (EFI_ERROR (Status)) {
80 return Status;
81 }
82
83 //
84 // If anything fails from here on, we must release the ring resources.
85 //
86 Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
87 if (EFI_ERROR (Status)) {
88 goto ReleaseQueue;
89 }
90
91 //
92 // Additional steps for MMIO: align the queue appropriately, and set the
93 // size. If anything fails from here on, we must unmap the ring resources.
94 //
95 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
96 if (EFI_ERROR (Status)) {
97 goto UnmapQueue;
98 }
99
100 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
101 if (EFI_ERROR (Status)) {
102 goto UnmapQueue;
103 }
104
105 //
106 // step 4c -- report GPFN (guest-physical frame number) of queue
107 //
108 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
109 if (EFI_ERROR (Status)) {
110 goto UnmapQueue;
111 }
112
113 *Mapping = MapInfo;
114
115 return EFI_SUCCESS;
116
117 UnmapQueue:
118 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
119
120 ReleaseQueue:
121 VirtioRingUninit (Dev->VirtIo, Ring);
122
123 return Status;
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 STATIC
152 EFI_STATUS
153 EFIAPI
154 VirtioNetInitTx (
155 IN OUT VNET_DEV *Dev
156 )
157 {
158 UINTN TxSharedReqSize;
159 UINTN PktIdx;
160 EFI_STATUS Status;
161 EFI_PHYSICAL_ADDRESS DeviceAddress;
162 VOID *TxSharedReqBuffer;
163
164 Dev->TxMaxPending = (UINT16)MIN (
165 Dev->TxRing.QueueSize / 2,
166 VNET_MAX_PENDING
167 );
168 Dev->TxCurPending = 0;
169 Dev->TxFreeStack = AllocatePool (
170 Dev->TxMaxPending *
171 sizeof *Dev->TxFreeStack
172 );
173 if (Dev->TxFreeStack == NULL) {
174 return EFI_OUT_OF_RESOURCES;
175 }
176
177 Dev->TxBufCollection = OrderedCollectionInit (
178 VirtioNetTxBufMapInfoCompare,
179 VirtioNetTxBufDeviceAddressCompare
180 );
181 if (Dev->TxBufCollection == NULL) {
182 Status = EFI_OUT_OF_RESOURCES;
183 goto FreeTxFreeStack;
184 }
185
186 //
187 // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it
188 // can be accessed equally by both processor and device.
189 //
190 Status = Dev->VirtIo->AllocateSharedPages (
191 Dev->VirtIo,
192 EFI_SIZE_TO_PAGES (sizeof *Dev->TxSharedReq),
193 &TxSharedReqBuffer
194 );
195 if (EFI_ERROR (Status)) {
196 goto UninitTxBufCollection;
197 }
198
199 ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);
200
201 Status = VirtioMapAllBytesInSharedBuffer (
202 Dev->VirtIo,
203 VirtioOperationBusMasterCommonBuffer,
204 TxSharedReqBuffer,
205 sizeof *(Dev->TxSharedReq),
206 &DeviceAddress,
207 &Dev->TxSharedReqMap
208 );
209 if (EFI_ERROR (Status)) {
210 goto FreeTxSharedReqBuffer;
211 }
212
213 Dev->TxSharedReq = TxSharedReqBuffer;
214
215 //
216 // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
217 // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
218 //
219 TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
220 sizeof (Dev->TxSharedReq->V0_9_5) :
221 sizeof *Dev->TxSharedReq;
222
223 for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
224 UINT16 DescIdx;
225
226 DescIdx = (UINT16)(2 * PktIdx);
227 Dev->TxFreeStack[PktIdx] = DescIdx;
228
229 //
230 // For each possibly pending packet, lay out the descriptor for the common
231 // (unmodified by the host) virtio-net request header.
232 //
233 Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;
234 Dev->TxRing.Desc[DescIdx].Len = (UINT32)TxSharedReqSize;
235 Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
236 Dev->TxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);
237
238 //
239 // The second descriptor of each pending TX packet is updated on the fly,
240 // but it always terminates the descriptor chain of the packet.
241 //
242 Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
243 }
244
245 //
246 // virtio-0.9.5, Appendix C, Packet Transmission
247 //
248 Dev->TxSharedReq->V0_9_5.Flags = 0;
249 Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
250
251 //
252 // For VirtIo 1.0 only -- the field exists, but it is unused
253 //
254 Dev->TxSharedReq->NumBuffers = 0;
255
256 //
257 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
258 //
259 MemoryFence ();
260 Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
261 ASSERT (Dev->TxLastUsed == 0);
262
263 //
264 // want no interrupt when a transmit completes
265 //
266 *Dev->TxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;
267
268 return EFI_SUCCESS;
269
270 FreeTxSharedReqBuffer:
271 Dev->VirtIo->FreeSharedPages (
272 Dev->VirtIo,
273 EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
274 TxSharedReqBuffer
275 );
276
277 UninitTxBufCollection:
278 OrderedCollectionUninit (Dev->TxBufCollection);
279
280 FreeTxFreeStack:
281 FreePool (Dev->TxFreeStack);
282
283 return Status;
284 }
285
286 /**
287 Set up static scaffolding for the VirtioNetReceive() SNP method and enable
288 live device operation.
289
290 This function may only be called as VirtioNetInitialize()'s final step.
291
292 The structures laid out and resources configured include:
293 - destination area for the host to write virtio-net request headers and
294 packet data into,
295 - select polling over RX interrupt,
296 - fully populate the RX queue with a static pattern of virtio descriptor
297 chains.
298
299 @param[in,out] Dev The VNET_DEV driver instance about to enter the
300 EfiSimpleNetworkInitialized state.
301
302 @return Status codes from VIRTIO_CFG_WRITE() or
303 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
304 VirtioMapAllBytesInSharedBuffer().
305 @retval EFI_SUCCESS RX setup successful. The device is live and may
306 already be writing to the receive area.
307 */
308 STATIC
309 EFI_STATUS
310 EFIAPI
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 Resets a network adapter and allocates the transmit and receive buffers
460 required by the network interface; optionally, also requests allocation of
461 additional transmit and receive buffers.
462
463 @param This The protocol instance pointer.
464 @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer
465 space that the driver should allocate for the
466 network interface. Some network interfaces will not
467 be able to use the extra buffer, and the caller
468 will not know if it is actually being used.
469 @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
470 space that the driver should allocate for the
471 network interface. Some network interfaces will not
472 be able to use the extra buffer, and the caller
473 will not know if it is actually being used.
474
475 @retval EFI_SUCCESS The network interface was initialized.
476 @retval EFI_NOT_STARTED The network interface has not been started.
477 @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit
478 and receive buffers.
479 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
480 unsupported value.
481 @retval EFI_DEVICE_ERROR The command could not be sent to the network
482 interface.
483 @retval EFI_UNSUPPORTED This function is not supported by the network
484 interface.
485
486 **/
487 EFI_STATUS
488 EFIAPI
489 VirtioNetInitialize (
490 IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
491 IN UINTN ExtraRxBufferSize OPTIONAL,
492 IN UINTN ExtraTxBufferSize OPTIONAL
493 )
494 {
495 VNET_DEV *Dev;
496 EFI_TPL OldTpl;
497 EFI_STATUS Status;
498 UINT8 NextDevStat;
499 UINT64 Features;
500
501 if (This == NULL) {
502 return EFI_INVALID_PARAMETER;
503 }
504
505 if ((ExtraRxBufferSize > 0) || (ExtraTxBufferSize > 0)) {
506 return EFI_UNSUPPORTED;
507 }
508
509 Dev = VIRTIO_NET_FROM_SNP (This);
510 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
511 if (Dev->Snm.State != EfiSimpleNetworkStarted) {
512 Status = EFI_NOT_STARTED;
513 goto InitFailed;
514 }
515
516 //
517 // In the EfiSimpleNetworkStarted state the virtio-net device has status
518 // value 0 (= reset) -- see the state diagram, the full call chain to
519 // the end of VirtioNetGetFeatures() (considering we're here now),
520 // the DeviceFailed label below, and VirtioNetShutdown().
521 //
522 // Accordingly, the below is a subsequence of the steps found in the
523 // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
524 //
525 NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
526 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
527 if (EFI_ERROR (Status)) {
528 goto InitFailed;
529 }
530
531 NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
532 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
533 if (EFI_ERROR (Status)) {
534 goto DeviceFailed;
535 }
536
537 //
538 // Set Page Size - MMIO VirtIo Specific
539 //
540 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
541 if (EFI_ERROR (Status)) {
542 goto DeviceFailed;
543 }
544
545 //
546 // step 4a -- retrieve features. Note that we're past validating required
547 // features in VirtioNetGetFeatures().
548 //
549 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
550 if (EFI_ERROR (Status)) {
551 goto DeviceFailed;
552 }
553
554 ASSERT (Features & VIRTIO_NET_F_MAC);
555 ASSERT (
556 Dev->Snm.MediaPresentSupported ==
557 !!(Features & VIRTIO_NET_F_STATUS)
558 );
559
560 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |
561 VIRTIO_F_IOMMU_PLATFORM;
562
563 //
564 // In virtio-1.0, feature negotiation is expected to complete before queue
565 // discovery, and the device can also reject the selected set of features.
566 //
567 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
568 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
569 if (EFI_ERROR (Status)) {
570 goto DeviceFailed;
571 }
572 }
573
574 //
575 // step 4b, 4c -- allocate and report virtqueues
576 //
577 Status = VirtioNetInitRing (
578 Dev,
579 VIRTIO_NET_Q_RX,
580 &Dev->RxRing,
581 &Dev->RxRingMap
582 );
583 if (EFI_ERROR (Status)) {
584 goto DeviceFailed;
585 }
586
587 Status = VirtioNetInitRing (
588 Dev,
589 VIRTIO_NET_Q_TX,
590 &Dev->TxRing,
591 &Dev->TxRingMap
592 );
593 if (EFI_ERROR (Status)) {
594 goto ReleaseRxRing;
595 }
596
597 //
598 // step 5 -- keep only the features we want
599 //
600 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
601 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
602 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
603 if (EFI_ERROR (Status)) {
604 goto ReleaseTxRing;
605 }
606 }
607
608 //
609 // step 6 -- virtio-net initialization complete
610 //
611 NextDevStat |= VSTAT_DRIVER_OK;
612 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
613 if (EFI_ERROR (Status)) {
614 goto ReleaseTxRing;
615 }
616
617 Status = VirtioNetInitTx (Dev);
618 if (EFI_ERROR (Status)) {
619 goto AbortDevice;
620 }
621
622 //
623 // start receiving
624 //
625 Status = VirtioNetInitRx (Dev);
626 if (EFI_ERROR (Status)) {
627 goto ReleaseTxAux;
628 }
629
630 Dev->Snm.State = EfiSimpleNetworkInitialized;
631 gBS->RestoreTPL (OldTpl);
632 return EFI_SUCCESS;
633
634 ReleaseTxAux:
635 VirtioNetShutdownTx (Dev);
636
637 AbortDevice:
638 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
639
640 ReleaseTxRing:
641 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
642
643 ReleaseRxRing:
644 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
645
646 DeviceFailed:
647 //
648 // restore device status invariant for the EfiSimpleNetworkStarted state
649 //
650 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
651
652 InitFailed:
653 gBS->RestoreTPL (OldTpl);
654 return Status;
655 }