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