]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / OvmfPkg / VirtioNetDxe / SnpInitialize.c
CommitLineData
12384f9b
LE
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
fc2c1543 8 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\r
12384f9b 9\r
b26f0cf9 10 SPDX-License-Identifier: BSD-2-Clause-Patent\r
12384f9b
LE
11\r
12**/\r
13\r
14#include <Library/BaseLib.h>\r
891f016c 15#include <Library/BaseMemoryLib.h>\r
12384f9b
LE
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
940baec0 33 @param[out] Mapping A resulting token to pass to VirtioNetUninitRing()\r
12384f9b
LE
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
940baec0
BS
38 VIRTIO_CFG_READ(), VirtioRingInit() and\r
39 VirtioRingMap().\r
12384f9b
LE
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
940baec0
BS
49 OUT VRING *Ring,\r
50 OUT VOID **Mapping\r
12384f9b
LE
51 )\r
52{\r
53 EFI_STATUS Status;\r
54 UINT16 QueueSize;\r
940baec0
BS
55 UINT64 RingBaseShift;\r
56 VOID *MapInfo;\r
12384f9b
LE
57\r
58 //\r
59 // step 4b -- allocate selected queue\r
60 //\r
56f65ed8 61 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);\r
12384f9b
LE
62 if (EFI_ERROR (Status)) {\r
63 return Status;\r
64 }\r
56f65ed8 65 Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
12384f9b
LE
66 if (EFI_ERROR (Status)) {\r
67 return Status;\r
68 }\r
56f65ed8 69\r
12384f9b
LE
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
fc2c1543 77 Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);\r
12384f9b
LE
78 if (EFI_ERROR (Status)) {\r
79 return Status;\r
80 }\r
81\r
940baec0
BS
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
56f65ed8
OM
90 //\r
91 // Additional steps for MMIO: align the queue appropriately, and set the\r
940baec0 92 // size. If anything fails from here on, we must unmap the ring resources.\r
56f65ed8
OM
93 //\r
94 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
95 if (EFI_ERROR (Status)) {\r
940baec0 96 goto UnmapQueue;\r
56f65ed8
OM
97 }\r
98\r
99 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
100 if (EFI_ERROR (Status)) {\r
940baec0 101 goto UnmapQueue;\r
56f65ed8
OM
102 }\r
103\r
12384f9b
LE
104 //\r
105 // step 4c -- report GPFN (guest-physical frame number) of queue\r
106 //\r
940baec0 107 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);\r
12384f9b 108 if (EFI_ERROR (Status)) {\r
940baec0 109 goto UnmapQueue;\r
12384f9b 110 }\r
56f65ed8 111\r
940baec0
BS
112 *Mapping = MapInfo;\r
113\r
56f65ed8
OM
114 return EFI_SUCCESS;\r
115\r
940baec0
BS
116UnmapQueue:\r
117 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);\r
118\r
56f65ed8 119ReleaseQueue:\r
fc2c1543 120 VirtioRingUninit (Dev->VirtIo, Ring);\r
56f65ed8 121\r
12384f9b
LE
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
bd114d9f
BS
144 of free descriptor chains or failed to init\r
145 TxBufCollection.\r
891f016c
BS
146 @return Status codes from VIRTIO_DEVICE_PROTOCOL.\r
147 AllocateSharedPages() or\r
148 VirtioMapAllBytesInSharedBuffer()\r
12384f9b
LE
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
891f016c
BS
159 UINTN TxSharedReqSize;\r
160 UINTN PktIdx;\r
161 EFI_STATUS Status;\r
162 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
163 VOID *TxSharedReqBuffer;\r
12384f9b 164\r
9f3acbb5
LE
165 Dev->TxMaxPending = (UINT16) MIN (Dev->TxRing.QueueSize / 2,\r
166 VNET_MAX_PENDING);\r
12384f9b
LE
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
bd114d9f
BS
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
891f016c
BS
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
bd114d9f 193 goto UninitTxBufCollection;\r
891f016c
BS
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
c6e2d064
LE
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
891f016c
BS
218 sizeof (Dev->TxSharedReq->V0_9_5) :\r
219 sizeof *Dev->TxSharedReq;\r
c6e2d064 220\r
12384f9b
LE
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
891f016c 231 Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;\r
c6e2d064 232 Dev->TxRing.Desc[DescIdx].Len = (UINT32) TxSharedReqSize;\r
12384f9b 233 Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;\r
9f3acbb5 234 Dev->TxRing.Desc[DescIdx].Next = (UINT16) (DescIdx + 1);\r
12384f9b
LE
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
891f016c
BS
246 Dev->TxSharedReq->V0_9_5.Flags = 0;\r
247 Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;\r
c6e2d064
LE
248\r
249 //\r
250 // For VirtIo 1.0 only -- the field exists, but it is unused\r
251 //\r
891f016c 252 Dev->TxSharedReq->NumBuffers = 0;\r
12384f9b
LE
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
891f016c
BS
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
bd114d9f
BS
274\r
275UninitTxBufCollection:\r
276 OrderedCollectionUninit (Dev->TxBufCollection);\r
277\r
891f016c
BS
278FreeTxFreeStack:\r
279 FreePool (Dev->TxFreeStack);\r
280\r
281 return Status;\r
12384f9b
LE
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
46b11f00
BS
301 @return Status codes from VIRTIO_CFG_WRITE() or\r
302 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or\r
303 VirtioMapAllBytesInSharedBuffer().\r
12384f9b
LE
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
46b11f00
BS
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
12384f9b 324\r
c6e2d064
LE
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
12384f9b
LE
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
c6e2d064 339 RxBufSize = VirtioNetReqSize +\r
12384f9b
LE
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
9f3acbb5 346 RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);\r
12384f9b 347\r
46b11f00
BS
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
12384f9b
LE
377 }\r
378\r
46b11f00
BS
379 Dev->RxBuf = RxBuffer;\r
380\r
12384f9b
LE
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
46b11f00 400 RxBufDeviceAddress = Dev->RxBufDeviceBase;\r
12384f9b
LE
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
46b11f00 411 Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;\r
c6e2d064 412 Dev->RxRing.Desc[DescIdx].Len = (UINT32) VirtioNetReqSize;\r
12384f9b 413 Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;\r
9f3acbb5 414 Dev->RxRing.Desc[DescIdx].Next = (UINT16) (DescIdx + 1);\r
46b11f00 415 RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;\r
12384f9b 416\r
46b11f00 417 Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;\r
c6e2d064 418 Dev->RxRing.Desc[DescIdx].Len = (UINT32) (RxBufSize - VirtioNetReqSize);\r
12384f9b 419 Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;\r
46b11f00 420 RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;\r
12384f9b
LE
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
56f65ed8 438 Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);\r
12384f9b 439 if (EFI_ERROR (Status)) {\r
56f65ed8 440 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
46b11f00 441 goto UnmapSharedBuffer;\r
12384f9b
LE
442 }\r
443\r
444 return Status;\r
46b11f00
BS
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
12384f9b
LE
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
bc8fde6f 501 UINT64 Features;\r
12384f9b
LE
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
56f65ed8 527 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
12384f9b
LE
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
56f65ed8
OM
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
12384f9b
LE
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
56f65ed8 550 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
12384f9b
LE
551 if (EFI_ERROR (Status)) {\r
552 goto DeviceFailed;\r
553 }\r
56f65ed8 554\r
12384f9b
LE
555 ASSERT (Features & VIRTIO_NET_F_MAC);\r
556 ASSERT (Dev->Snm.MediaPresentSupported ==\r
557 !!(Features & VIRTIO_NET_F_STATUS));\r
558\r
f60ed5ab
BS
559 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |\r
560 VIRTIO_F_IOMMU_PLATFORM;\r
33c6b934
LE
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
12384f9b
LE
573 //\r
574 // step 4b, 4c -- allocate and report virtqueues\r
575 //\r
940baec0
BS
576 Status = VirtioNetInitRing (\r
577 Dev,\r
578 VIRTIO_NET_Q_RX,\r
579 &Dev->RxRing,\r
580 &Dev->RxRingMap\r
581 );\r
12384f9b
LE
582 if (EFI_ERROR (Status)) {\r
583 goto DeviceFailed;\r
584 }\r
585\r
940baec0
BS
586 Status = VirtioNetInitRing (\r
587 Dev,\r
588 VIRTIO_NET_Q_TX,\r
589 &Dev->TxRing,\r
590 &Dev->TxRingMap\r
591 );\r
12384f9b
LE
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
33c6b934 599 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
f60ed5ab 600 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
33c6b934
LE
601 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
602 if (EFI_ERROR (Status)) {\r
603 goto ReleaseTxRing;\r
604 }\r
12384f9b
LE
605 }\r
606\r
607 //\r
608 // step 6 -- virtio-net initialization complete\r
609 //\r
610 NextDevStat |= VSTAT_DRIVER_OK;\r
56f65ed8 611 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
12384f9b
LE
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
56f65ed8 637 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
12384f9b
LE
638\r
639ReleaseTxRing:\r
940baec0 640 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);\r
12384f9b
LE
641\r
642ReleaseRxRing:\r
940baec0 643 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);\r
12384f9b
LE
644\r
645DeviceFailed:\r
646 //\r
647 // restore device status invariant for the EfiSimpleNetworkStarted state\r
648 //\r
56f65ed8 649 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
12384f9b
LE
650\r
651InitFailed:\r
652 gBS->RestoreTPL (OldTpl);\r
653 return Status;\r
654}\r