TianoCore EDK2 master
Loading...
Searching...
No Matches
SnpInitialize.c
Go to the documentation of this file.
1
14#include <Library/BaseLib.h>
18
19#include "VirtioNet.h"
20
44EFIAPI
46 IN OUT VNET_DEV *Dev,
47 IN UINT16 Selector,
48 OUT VRING *Ring,
49 OUT VOID **Mapping
50 )
51{
52 EFI_STATUS Status;
53 UINT16 QueueSize;
54 UINT64 RingBaseShift;
55 VOID *MapInfo;
56
57 //
58 // step 4b -- allocate selected queue
59 //
60 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
61 if (EFI_ERROR (Status)) {
62 return Status;
63 }
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
78 Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
79 if (EFI_ERROR (Status)) {
80 return Status;
81 }
82
83 //
84 // If anything fails from here on, we must release the ring resources.
85 //
86 Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
87 if (EFI_ERROR (Status)) {
88 goto ReleaseQueue;
89 }
90
91 //
92 // Additional steps for MMIO: align the queue appropriately, and set the
93 // size. If anything fails from here on, we must unmap the ring resources.
94 //
95 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
96 if (EFI_ERROR (Status)) {
97 goto UnmapQueue;
98 }
99
100 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
101 if (EFI_ERROR (Status)) {
102 goto UnmapQueue;
103 }
104
105 //
106 // step 4c -- report GPFN (guest-physical frame number) of queue
107 //
108 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
109 if (EFI_ERROR (Status)) {
110 goto UnmapQueue;
111 }
112
113 *Mapping = MapInfo;
114
115 return EFI_SUCCESS;
116
117UnmapQueue:
118 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
119
120ReleaseQueue:
121 VirtioRingUninit (Dev->VirtIo, Ring);
122
123 return Status;
124}
125
151STATIC
153EFIAPI
155 IN OUT VNET_DEV *Dev
156 )
157{
158 UINTN TxSharedReqSize;
159 UINTN PktIdx;
160 EFI_STATUS Status;
161 EFI_PHYSICAL_ADDRESS DeviceAddress;
162 VOID *TxSharedReqBuffer;
163
164 Dev->TxMaxPending = (UINT16)MIN (
165 Dev->TxRing.QueueSize / 2,
166 VNET_MAX_PENDING
167 );
168 Dev->TxCurPending = 0;
169 Dev->TxFreeStack = AllocatePool (
170 Dev->TxMaxPending *
171 sizeof *Dev->TxFreeStack
172 );
173 if (Dev->TxFreeStack == NULL) {
174 return EFI_OUT_OF_RESOURCES;
175 }
176
177 Dev->TxBufCollection = OrderedCollectionInit (
180 );
181 if (Dev->TxBufCollection == NULL) {
182 Status = EFI_OUT_OF_RESOURCES;
183 goto FreeTxFreeStack;
184 }
185
186 //
187 // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it
188 // can be accessed equally by both processor and device.
189 //
190 Status = Dev->VirtIo->AllocateSharedPages (
191 Dev->VirtIo,
192 EFI_SIZE_TO_PAGES (sizeof *Dev->TxSharedReq),
193 &TxSharedReqBuffer
194 );
195 if (EFI_ERROR (Status)) {
196 goto UninitTxBufCollection;
197 }
198
199 ZeroMem (TxSharedReqBuffer, sizeof *Dev->TxSharedReq);
200
202 Dev->VirtIo,
203 VirtioOperationBusMasterCommonBuffer,
204 TxSharedReqBuffer,
205 sizeof *(Dev->TxSharedReq),
206 &DeviceAddress,
207 &Dev->TxSharedReqMap
208 );
209 if (EFI_ERROR (Status)) {
210 goto FreeTxSharedReqBuffer;
211 }
212
213 Dev->TxSharedReq = TxSharedReqBuffer;
214
215 //
216 // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
217 // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
218 //
219 TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
220 sizeof (Dev->TxSharedReq->V0_9_5) :
221 sizeof *Dev->TxSharedReq;
222
223 for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
224 UINT16 DescIdx;
225
226 DescIdx = (UINT16)(2 * PktIdx);
227 Dev->TxFreeStack[PktIdx] = DescIdx;
228
229 //
230 // For each possibly pending packet, lay out the descriptor for the common
231 // (unmodified by the host) virtio-net request header.
232 //
233 Dev->TxRing.Desc[DescIdx].Addr = DeviceAddress;
234 Dev->TxRing.Desc[DescIdx].Len = (UINT32)TxSharedReqSize;
235 Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
236 Dev->TxRing.Desc[DescIdx].Next = (UINT16)(DescIdx + 1);
237
238 //
239 // The second descriptor of each pending TX packet is updated on the fly,
240 // but it always terminates the descriptor chain of the packet.
241 //
242 Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
243 }
244
245 //
246 // virtio-0.9.5, Appendix C, Packet Transmission
247 //
248 Dev->TxSharedReq->V0_9_5.Flags = 0;
249 Dev->TxSharedReq->V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
250
251 //
252 // For VirtIo 1.0 only -- the field exists, but it is unused
253 //
254 Dev->TxSharedReq->NumBuffers = 0;
255
256 //
257 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
258 //
259 MemoryFence ();
260 Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
261 ASSERT (Dev->TxLastUsed == 0);
262
263 //
264 // want no interrupt when a transmit completes
265 //
266 *Dev->TxRing.Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;
267
268 return EFI_SUCCESS;
269
270FreeTxSharedReqBuffer:
271 Dev->VirtIo->FreeSharedPages (
272 Dev->VirtIo,
273 EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
274 TxSharedReqBuffer
275 );
276
277UninitTxBufCollection:
278 OrderedCollectionUninit (Dev->TxBufCollection);
279
280FreeTxFreeStack:
281 FreePool (Dev->TxFreeStack);
282
283 return Status;
284}
285
308STATIC
310EFIAPI
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
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
446UnmapSharedBuffer:
447 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
448
449FreeSharedBuffer:
450 Dev->VirtIo->FreeSharedPages (
451 Dev->VirtIo,
452 Dev->RxBufNrPages,
453 RxBuffer
454 );
455 return Status;
456}
457
488EFIAPI
491 IN UINTN ExtraRxBufferSize OPTIONAL,
492 IN UINTN ExtraTxBufferSize OPTIONAL
493 )
494{
495 VNET_DEV *Dev;
496 EFI_TPL OldTpl;
497 EFI_STATUS Status;
498 UINT8 NextDevStat;
499 UINT64 Features;
500
501 if (This == NULL) {
502 return EFI_INVALID_PARAMETER;
503 }
504
505 if ((ExtraRxBufferSize > 0) || (ExtraTxBufferSize > 0)) {
506 return EFI_UNSUPPORTED;
507 }
508
509 Dev = VIRTIO_NET_FROM_SNP (This);
510 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
511 if (Dev->Snm.State != EfiSimpleNetworkStarted) {
512 Status = EFI_NOT_STARTED;
513 goto InitFailed;
514 }
515
516 //
517 // In the EfiSimpleNetworkStarted state the virtio-net device has status
518 // value 0 (= reset) -- see the state diagram, the full call chain to
519 // the end of VirtioNetGetFeatures() (considering we're here now),
520 // the DeviceFailed label below, and VirtioNetShutdown().
521 //
522 // Accordingly, the below is a subsequence of the steps found in the
523 // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
524 //
525 NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
526 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
527 if (EFI_ERROR (Status)) {
528 goto InitFailed;
529 }
530
531 NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
532 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
533 if (EFI_ERROR (Status)) {
534 goto DeviceFailed;
535 }
536
537 //
538 // Set Page Size - MMIO VirtIo Specific
539 //
540 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
541 if (EFI_ERROR (Status)) {
542 goto DeviceFailed;
543 }
544
545 //
546 // step 4a -- retrieve features. Note that we're past validating required
547 // features in VirtioNetGetFeatures().
548 //
549 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
550 if (EFI_ERROR (Status)) {
551 goto DeviceFailed;
552 }
553
554 ASSERT (Features & VIRTIO_NET_F_MAC);
555 ASSERT (
556 Dev->Snm.MediaPresentSupported ==
557 !!(Features & VIRTIO_NET_F_STATUS)
558 );
559
560 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1 |
561 VIRTIO_F_IOMMU_PLATFORM;
562
563 //
564 // In virtio-1.0, feature negotiation is expected to complete before queue
565 // discovery, and the device can also reject the selected set of features.
566 //
567 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
568 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
569 if (EFI_ERROR (Status)) {
570 goto DeviceFailed;
571 }
572 }
573
574 //
575 // step 4b, 4c -- allocate and report virtqueues
576 //
577 Status = VirtioNetInitRing (
578 Dev,
579 VIRTIO_NET_Q_RX,
580 &Dev->RxRing,
581 &Dev->RxRingMap
582 );
583 if (EFI_ERROR (Status)) {
584 goto DeviceFailed;
585 }
586
587 Status = VirtioNetInitRing (
588 Dev,
589 VIRTIO_NET_Q_TX,
590 &Dev->TxRing,
591 &Dev->TxRingMap
592 );
593 if (EFI_ERROR (Status)) {
594 goto ReleaseRxRing;
595 }
596
597 //
598 // step 5 -- keep only the features we want
599 //
600 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
601 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
602 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
603 if (EFI_ERROR (Status)) {
604 goto ReleaseTxRing;
605 }
606 }
607
608 //
609 // step 6 -- virtio-net initialization complete
610 //
611 NextDevStat |= VSTAT_DRIVER_OK;
612 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
613 if (EFI_ERROR (Status)) {
614 goto ReleaseTxRing;
615 }
616
617 Status = VirtioNetInitTx (Dev);
618 if (EFI_ERROR (Status)) {
619 goto AbortDevice;
620 }
621
622 //
623 // start receiving
624 //
625 Status = VirtioNetInitRx (Dev);
626 if (EFI_ERROR (Status)) {
627 goto ReleaseTxAux;
628 }
629
630 Dev->Snm.State = EfiSimpleNetworkInitialized;
631 gBS->RestoreTPL (OldTpl);
632 return EFI_SUCCESS;
633
634ReleaseTxAux:
635 VirtioNetShutdownTx (Dev);
636
637AbortDevice:
638 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
639
640ReleaseTxRing:
641 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
642
643ReleaseRxRing:
644 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
645
646DeviceFailed:
647 //
648 // restore device status invariant for the EfiSimpleNetworkStarted state
649 //
650 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
651
652InitFailed:
653 gBS->RestoreTPL (OldTpl);
654 return Status;
655}
UINT64 UINTN
VOID EFIAPI MemoryFence(VOID)
Definition: CpuBreakpoint.c:42
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
VOID EFIAPI OrderedCollectionUninit(IN ORDERED_COLLECTION *Collection)
ORDERED_COLLECTION *EFIAPI OrderedCollectionInit(IN ORDERED_COLLECTION_USER_COMPARE UserStructCompare, IN ORDERED_COLLECTION_KEY_COMPARE KeyCompare)
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
EFI_STATUS EFIAPI VirtioNetInitialize(IN EFI_SIMPLE_NETWORK_PROTOCOL *This, IN UINTN ExtraRxBufferSize OPTIONAL, IN UINTN ExtraTxBufferSize OPTIONAL)
STATIC EFI_STATUS EFIAPI VirtioNetInitTx(IN OUT VNET_DEV *Dev)
STATIC EFI_STATUS EFIAPI VirtioNetInitRing(IN OUT VNET_DEV *Dev, IN UINT16 Selector, OUT VRING *Ring, OUT VOID **Mapping)
Definition: SnpInitialize.c:45
STATIC EFI_STATUS EFIAPI VirtioNetInitRx(IN OUT VNET_DEV *Dev)
INTN EFIAPI VirtioNetTxBufMapInfoCompare(IN CONST VOID *UserStruct1, IN CONST VOID *UserStruct2)
INTN EFIAPI VirtioNetTxBufDeviceAddressCompare(IN CONST VOID *StandaloneKey, IN CONST VOID *UserStruct)
VOID EFIAPI VirtioNetUninitRing(IN OUT VNET_DEV *Dev, IN OUT VRING *Ring, IN VOID *RingMap)
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
UINTN EFI_TPL
Definition: UefiBaseType.h:41
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
EFI_STATUS EFIAPI VirtioMapAllBytesInSharedBuffer(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN VIRTIO_MAP_OPERATION Operation, IN VOID *HostAddress, IN UINTN NumberOfBytes, OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping)
Definition: VirtioLib.c:469
EFI_STATUS EFIAPI VirtioRingMap(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN VRING *Ring, OUT UINT64 *RingBaseShift, OUT VOID **Mapping)
Definition: VirtioLib.c:529
EFI_STATUS EFIAPI Virtio10WriteFeatures(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN UINT64 Features, IN OUT UINT8 *DeviceStatus)
Definition: VirtioLib.c:391
EFI_STATUS EFIAPI VirtioRingInit(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN UINT16 QueueSize, OUT VRING *Ring)
Definition: VirtioLib.c:49
VOID EFIAPI VirtioRingUninit(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN OUT VRING *Ring)
Definition: VirtioLib.c:144