]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg: Apply uncrustify changes
[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
12384f9b
LE
42STATIC\r
43EFI_STATUS\r
44EFIAPI\r
45VirtioNetInitRing (\r
ac0a286f
MK
46 IN OUT VNET_DEV *Dev,\r
47 IN UINT16 Selector,\r
48 OUT VRING *Ring,\r
49 OUT VOID **Mapping\r
12384f9b
LE
50 )\r
51{\r
ac0a286f
MK
52 EFI_STATUS Status;\r
53 UINT16 QueueSize;\r
54 UINT64 RingBaseShift;\r
55 VOID *MapInfo;\r
12384f9b
LE
56\r
57 //\r
58 // step 4b -- allocate selected queue\r
59 //\r
56f65ed8 60 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);\r
12384f9b
LE
61 if (EFI_ERROR (Status)) {\r
62 return Status;\r
63 }\r
ac0a286f 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
ac0a286f 77\r
fc2c1543 78 Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);\r
12384f9b
LE
79 if (EFI_ERROR (Status)) {\r
80 return Status;\r
81 }\r
82\r
940baec0
BS
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
56f65ed8
OM
91 //\r
92 // Additional steps for MMIO: align the queue appropriately, and set the\r
940baec0 93 // size. If anything fails from here on, we must unmap the ring resources.\r
56f65ed8
OM
94 //\r
95 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
96 if (EFI_ERROR (Status)) {\r
940baec0 97 goto UnmapQueue;\r
56f65ed8
OM
98 }\r
99\r
100 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
101 if (EFI_ERROR (Status)) {\r
940baec0 102 goto UnmapQueue;\r
56f65ed8
OM
103 }\r
104\r
12384f9b
LE
105 //\r
106 // step 4c -- report GPFN (guest-physical frame number) of queue\r
107 //\r
940baec0 108 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);\r
12384f9b 109 if (EFI_ERROR (Status)) {\r
940baec0 110 goto UnmapQueue;\r
12384f9b 111 }\r
56f65ed8 112\r
940baec0
BS
113 *Mapping = MapInfo;\r
114\r
56f65ed8
OM
115 return EFI_SUCCESS;\r
116\r
940baec0
BS
117UnmapQueue:\r
118 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);\r
119\r
56f65ed8 120ReleaseQueue:\r
fc2c1543 121 VirtioRingUninit (Dev->VirtIo, Ring);\r
56f65ed8 122\r
12384f9b
LE
123 return Status;\r
124}\r
125\r
12384f9b
LE
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
12384f9b
LE
151STATIC\r
152EFI_STATUS\r
153EFIAPI\r
154VirtioNetInitTx (\r
ac0a286f 155 IN OUT VNET_DEV *Dev\r
12384f9b
LE
156 )\r
157{\r
891f016c
BS
158 UINTN TxSharedReqSize;\r
159 UINTN PktIdx;\r
160 EFI_STATUS Status;\r
161 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
162 VOID *TxSharedReqBuffer;\r
12384f9b 163\r
ac0a286f
MK
164 Dev->TxMaxPending = (UINT16)MIN (\r
165 Dev->TxRing.QueueSize / 2,\r
166 VNET_MAX_PENDING\r
167 );\r
12384f9b 168 Dev->TxCurPending = 0;\r
ac0a286f
MK
169 Dev->TxFreeStack = AllocatePool (\r
170 Dev->TxMaxPending *\r
171 sizeof *Dev->TxFreeStack\r
172 );\r
12384f9b
LE
173 if (Dev->TxFreeStack == NULL) {\r
174 return EFI_OUT_OF_RESOURCES;\r
175 }\r
176\r
bd114d9f
BS
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
891f016c
BS
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
bd114d9f 196 goto UninitTxBufCollection;\r
891f016c
BS
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
c6e2d064
LE
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
891f016c
BS
220 sizeof (Dev->TxSharedReq->V0_9_5) :\r
221 sizeof *Dev->TxSharedReq;\r
c6e2d064 222\r
12384f9b 223 for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {\r
ac0a286f 224 UINT16 DescIdx;\r
12384f9b 225\r
ac0a286f 226 DescIdx = (UINT16)(2 * PktIdx);\r
12384f9b
LE
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
891f016c 233 Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;\r
ac0a286f 234 Dev->TxRing.Desc[DescIdx].Len = (UINT32)TxSharedReqSize;\r
12384f9b 235 Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;\r
ac0a286f 236 Dev->TxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);\r
12384f9b
LE
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
891f016c
BS
248 Dev->TxSharedReq->V0_9_5.Flags = 0;\r
249 Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;\r
c6e2d064
LE
250\r
251 //\r
252 // For VirtIo 1.0 only -- the field exists, but it is unused\r
253 //\r
891f016c 254 Dev->TxSharedReq->NumBuffers = 0;\r
12384f9b
LE
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
ac0a286f 266 *Dev->TxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;\r
12384f9b
LE
267\r
268 return EFI_SUCCESS;\r
891f016c
BS
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
bd114d9f
BS
276\r
277UninitTxBufCollection:\r
278 OrderedCollectionUninit (Dev->TxBufCollection);\r
279\r
891f016c
BS
280FreeTxFreeStack:\r
281 FreePool (Dev->TxFreeStack);\r
282\r
283 return Status;\r
12384f9b
LE
284}\r
285\r
12384f9b
LE
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
46b11f00
BS
302 @return Status codes from VIRTIO_CFG_WRITE() or\r
303 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or\r
304 VirtioMapAllBytesInSharedBuffer().\r
12384f9b
LE
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
12384f9b
LE
308STATIC\r
309EFI_STATUS\r
310EFIAPI\r
311VirtioNetInitRx (\r
ac0a286f 312 IN OUT VNET_DEV *Dev\r
12384f9b
LE
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
ac0a286f 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
ac0a286f 354 NumBytes = RxAlwaysPending * RxBufSize;\r
46b11f00 355 Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);\r
ac0a286f
MK
356 Status = Dev->VirtIo->AllocateSharedPages (\r
357 Dev->VirtIo,\r
358 Dev->RxBufNrPages,\r
359 &RxBuffer\r
360 );\r
46b11f00
BS
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
ac0a286f 393 *Dev->RxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;\r
12384f9b
LE
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
ac0a286f 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
ac0a286f 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
ac0a286f
MK
414 Dev->RxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);\r
415 RxBufDeviceAddress += Dev->RxRing.Desc[DescIdx++].Len;\r
12384f9b 416\r
46b11f00 417 Dev->RxRing.Desc[DescIdx].Addr = RxBufDeviceAddress;\r
ac0a286f 418 Dev->RxRing.Desc[DescIdx].Len = (UINT32)(RxBufSize - VirtioNetReqSize);\r
12384f9b 419 Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;\r
ac0a286f 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
12384f9b
LE
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
12384f9b
LE
487EFI_STATUS\r
488EFIAPI\r
489VirtioNetInitialize (\r
ac0a286f
MK
490 IN EFI_SIMPLE_NETWORK_PROTOCOL *This,\r
491 IN UINTN ExtraRxBufferSize OPTIONAL,\r
492 IN UINTN ExtraTxBufferSize OPTIONAL\r
12384f9b
LE
493 )\r
494{\r
ac0a286f
MK
495 VNET_DEV *Dev;\r
496 EFI_TPL OldTpl;\r
497 EFI_STATUS Status;\r
498 UINT8 NextDevStat;\r
499 UINT64 Features;\r
12384f9b
LE
500\r
501 if (This == NULL) {\r
502 return EFI_INVALID_PARAMETER;\r
503 }\r
ac0a286f
MK
504\r
505 if ((ExtraRxBufferSize > 0) || (ExtraTxBufferSize > 0)) {\r
12384f9b
LE
506 return EFI_UNSUPPORTED;\r
507 }\r
508\r
ac0a286f 509 Dev = VIRTIO_NET_FROM_SNP (This);\r
12384f9b
LE
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
ac0a286f 526 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
12384f9b
LE
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
ac0a286f 532 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
56f65ed8
OM
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
12384f9b
LE
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
56f65ed8 549 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
12384f9b
LE
550 if (EFI_ERROR (Status)) {\r
551 goto DeviceFailed;\r
552 }\r
56f65ed8 553\r
12384f9b 554 ASSERT (Features & VIRTIO_NET_F_MAC);\r
ac0a286f
MK
555 ASSERT (\r
556 Dev->Snm.MediaPresentSupported ==\r
557 !!(Features & VIRTIO_NET_F_STATUS)\r
558 );\r
12384f9b 559\r
f60ed5ab
BS
560 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |\r
561 VIRTIO_F_IOMMU_PLATFORM;\r
33c6b934
LE
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
12384f9b
LE
574 //\r
575 // step 4b, 4c -- allocate and report virtqueues\r
576 //\r
940baec0
BS
577 Status = VirtioNetInitRing (\r
578 Dev,\r
579 VIRTIO_NET_Q_RX,\r
580 &Dev->RxRing,\r
581 &Dev->RxRingMap\r
582 );\r
12384f9b
LE
583 if (EFI_ERROR (Status)) {\r
584 goto DeviceFailed;\r
585 }\r
586\r
940baec0
BS
587 Status = VirtioNetInitRing (\r
588 Dev,\r
589 VIRTIO_NET_Q_TX,\r
590 &Dev->TxRing,\r
591 &Dev->TxRingMap\r
592 );\r
12384f9b
LE
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
33c6b934 600 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
f60ed5ab 601 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
ac0a286f 602 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
33c6b934
LE
603 if (EFI_ERROR (Status)) {\r
604 goto ReleaseTxRing;\r
605 }\r
12384f9b
LE
606 }\r
607\r
608 //\r
609 // step 6 -- virtio-net initialization complete\r
610 //\r
611 NextDevStat |= VSTAT_DRIVER_OK;\r
ac0a286f 612 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
12384f9b
LE
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
56f65ed8 638 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
12384f9b
LE
639\r
640ReleaseTxRing:\r
940baec0 641 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);\r
12384f9b
LE
642\r
643ReleaseRxRing:\r
940baec0 644 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);\r
12384f9b
LE
645\r
646DeviceFailed:\r
647 //\r
648 // restore device status invariant for the EfiSimpleNetworkStarted state\r
649 //\r
56f65ed8 650 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
12384f9b
LE
651\r
652InitFailed:\r
653 gBS->RestoreTPL (OldTpl);\r
654 return Status;\r
655}\r