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