]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg: Update DSC/FDF to use NetworkPkg's include fragment file.
[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
43 STATIC
44 EFI_STATUS
45 EFIAPI
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
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
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
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 }