3 Implementation of the SNP.Initialize() function and its private helpers if
6 Copyright (C) 2013, Red Hat, Inc.
7 Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
19 #include "VirtioNet.h"
22 Initialize a virtio ring for a specific transfer direction of the virtio-net
25 This function may only be called by VirtioNetInitialize().
27 @param[in,out] Dev The VNET_DEV driver instance about to enter the
28 EfiSimpleNetworkInitialized state.
29 @param[in] Selector Identifies the transfer direction (virtio queue) of
31 @param[out] Ring The virtio-ring inside the VNET_DEV structure,
32 corresponding to Selector.
33 @param[out] Mapping A resulting token to pass to VirtioNetUninitRing()
35 @retval EFI_UNSUPPORTED The queue size reported by the virtio-net device is
37 @return Status codes from VIRTIO_CFG_WRITE(),
38 VIRTIO_CFG_READ(), VirtioRingInit() and
40 @retval EFI_SUCCESS Ring initialized.
58 // step 4b -- allocate selected queue
60 Status
= Dev
->VirtIo
->SetQueueSel (Dev
->VirtIo
, Selector
);
61 if (EFI_ERROR (Status
)) {
65 Status
= Dev
->VirtIo
->GetQueueNumMax (Dev
->VirtIo
, &QueueSize
);
66 if (EFI_ERROR (Status
)) {
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
75 return EFI_UNSUPPORTED
;
78 Status
= VirtioRingInit (Dev
->VirtIo
, QueueSize
, Ring
);
79 if (EFI_ERROR (Status
)) {
84 // If anything fails from here on, we must release the ring resources.
86 Status
= VirtioRingMap (Dev
->VirtIo
, Ring
, &RingBaseShift
, &MapInfo
);
87 if (EFI_ERROR (Status
)) {
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.
95 Status
= Dev
->VirtIo
->SetQueueNum (Dev
->VirtIo
, QueueSize
);
96 if (EFI_ERROR (Status
)) {
100 Status
= Dev
->VirtIo
->SetQueueAlign (Dev
->VirtIo
, EFI_PAGE_SIZE
);
101 if (EFI_ERROR (Status
)) {
106 // step 4c -- report GPFN (guest-physical frame number) of queue
108 Status
= Dev
->VirtIo
->SetQueueAddress (Dev
->VirtIo
, Ring
, RingBaseShift
);
109 if (EFI_ERROR (Status
)) {
118 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, MapInfo
);
121 VirtioRingUninit (Dev
->VirtIo
, Ring
);
127 Set up static scaffolding for the VirtioNetTransmit() and
128 VirtioNetGetStatus() SNP methods.
130 This function may only be called by VirtioNetInitialize().
132 The structures laid out and resources configured include:
133 - fully populate the TX queue with a static pattern of virtio descriptor
135 - tracking of heads of free descriptor chains from the above,
136 - one common virtio-net request header (never modified by the host) for all
138 - select polling over TX interrupt.
140 @param[in,out] Dev The VNET_DEV driver instance about to enter the
141 EfiSimpleNetworkInitialized state.
143 @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads
144 of free descriptor chains or failed to init
146 @return Status codes from VIRTIO_DEVICE_PROTOCOL.
147 AllocateSharedPages() or
148 VirtioMapAllBytesInSharedBuffer()
149 @retval EFI_SUCCESS TX setup successful.
158 UINTN TxSharedReqSize
;
161 EFI_PHYSICAL_ADDRESS DeviceAddress
;
162 VOID
*TxSharedReqBuffer
;
164 Dev
->TxMaxPending
= (UINT16
)MIN (
165 Dev
->TxRing
.QueueSize
/ 2,
168 Dev
->TxCurPending
= 0;
169 Dev
->TxFreeStack
= AllocatePool (
171 sizeof *Dev
->TxFreeStack
173 if (Dev
->TxFreeStack
== NULL
) {
174 return EFI_OUT_OF_RESOURCES
;
177 Dev
->TxBufCollection
= OrderedCollectionInit (
178 VirtioNetTxBufMapInfoCompare
,
179 VirtioNetTxBufDeviceAddressCompare
181 if (Dev
->TxBufCollection
== NULL
) {
182 Status
= EFI_OUT_OF_RESOURCES
;
183 goto FreeTxFreeStack
;
187 // Allocate TxSharedReq header and map with BusMasterCommonBuffer so that it
188 // can be accessed equally by both processor and device.
190 Status
= Dev
->VirtIo
->AllocateSharedPages (
192 EFI_SIZE_TO_PAGES (sizeof *Dev
->TxSharedReq
),
195 if (EFI_ERROR (Status
)) {
196 goto UninitTxBufCollection
;
199 ZeroMem (TxSharedReqBuffer
, sizeof *Dev
->TxSharedReq
);
201 Status
= VirtioMapAllBytesInSharedBuffer (
203 VirtioOperationBusMasterCommonBuffer
,
205 sizeof *(Dev
->TxSharedReq
),
209 if (EFI_ERROR (Status
)) {
210 goto FreeTxSharedReqBuffer
;
213 Dev
->TxSharedReq
= TxSharedReqBuffer
;
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.
219 TxSharedReqSize
= (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) ?
220 sizeof (Dev
->TxSharedReq
->V0_9_5
) :
221 sizeof *Dev
->TxSharedReq
;
223 for (PktIdx
= 0; PktIdx
< Dev
->TxMaxPending
; ++PktIdx
) {
226 DescIdx
= (UINT16
)(2 * PktIdx
);
227 Dev
->TxFreeStack
[PktIdx
] = DescIdx
;
230 // For each possibly pending packet, lay out the descriptor for the common
231 // (unmodified by the host) virtio-net request header.
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);
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.
242 Dev
->TxRing
.Desc
[DescIdx
+ 1].Flags
= 0;
246 // virtio-0.9.5, Appendix C, Packet Transmission
248 Dev
->TxSharedReq
->V0_9_5
.Flags
= 0;
249 Dev
->TxSharedReq
->V0_9_5
.GsoType
= VIRTIO_NET_HDR_GSO_NONE
;
252 // For VirtIo 1.0 only -- the field exists, but it is unused
254 Dev
->TxSharedReq
->NumBuffers
= 0;
257 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
260 Dev
->TxLastUsed
= *Dev
->TxRing
.Used
.Idx
;
261 ASSERT (Dev
->TxLastUsed
== 0);
264 // want no interrupt when a transmit completes
266 *Dev
->TxRing
.Avail
.Flags
= (UINT16
)VRING_AVAIL_F_NO_INTERRUPT
;
270 FreeTxSharedReqBuffer
:
271 Dev
->VirtIo
->FreeSharedPages (
273 EFI_SIZE_TO_PAGES (sizeof *(Dev
->TxSharedReq
)),
277 UninitTxBufCollection
:
278 OrderedCollectionUninit (Dev
->TxBufCollection
);
281 FreePool (Dev
->TxFreeStack
);
287 Set up static scaffolding for the VirtioNetReceive() SNP method and enable
288 live device operation.
290 This function may only be called as VirtioNetInitialize()'s final step.
292 The structures laid out and resources configured include:
293 - destination area for the host to write virtio-net request headers and
295 - select polling over RX interrupt,
296 - fully populate the RX queue with a static pattern of virtio descriptor
299 @param[in,out] Dev The VNET_DEV driver instance about to enter the
300 EfiSimpleNetworkInitialized state.
302 @return Status codes from VIRTIO_CFG_WRITE() or
303 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
304 VirtioMapAllBytesInSharedBuffer().
305 @retval EFI_SUCCESS RX setup successful. The device is live and may
306 already be writing to the receive area.
316 UINTN VirtioNetReqSize
;
318 UINT16 RxAlwaysPending
;
322 EFI_PHYSICAL_ADDRESS RxBufDeviceAddress
;
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.
329 VirtioNetReqSize
= (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) ?
330 sizeof (VIRTIO_NET_REQ
) :
331 sizeof (VIRTIO_1_0_NET_REQ
);
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).
339 RxBufSize
= VirtioNetReqSize
+
340 (Dev
->Snm
.MediaHeaderSize
+ Dev
->Snm
.MaxPacketSize
);
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.
346 RxAlwaysPending
= (UINT16
)MIN (Dev
->RxRing
.QueueSize
/ 2, VNET_MAX_PENDING
);
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
354 NumBytes
= RxAlwaysPending
* RxBufSize
;
355 Dev
->RxBufNrPages
= EFI_SIZE_TO_PAGES (NumBytes
);
356 Status
= Dev
->VirtIo
->AllocateSharedPages (
361 if (EFI_ERROR (Status
)) {
365 ZeroMem (RxBuffer
, NumBytes
);
367 Status
= VirtioMapAllBytesInSharedBuffer (
369 VirtioOperationBusMasterCommonBuffer
,
372 &Dev
->RxBufDeviceBase
,
375 if (EFI_ERROR (Status
)) {
376 goto FreeSharedBuffer
;
379 Dev
->RxBuf
= RxBuffer
;
382 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
385 Dev
->RxLastUsed
= *Dev
->RxRing
.Used
.Idx
;
386 ASSERT (Dev
->RxLastUsed
== 0);
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().
393 *Dev
->RxRing
.Avail
.Flags
= (UINT16
)VRING_AVAIL_F_NO_INTERRUPT
;
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
400 RxBufDeviceAddress
= Dev
->RxBufDeviceBase
;
401 for (PktIdx
= 0; PktIdx
< RxAlwaysPending
; ++PktIdx
) {
403 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
404 // invisible to the host until we update the Index Field
406 Dev
->RxRing
.Avail
.Ring
[PktIdx
] = DescIdx
;
409 // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
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
;
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
;
424 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
427 *Dev
->RxRing
.Avail
.Idx
= RxAlwaysPending
;
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.
435 // virtio-0.9.5, 2.4.1.4 Notifying the Device
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
;
447 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RxBufMap
);
450 Dev
->VirtIo
->FreeSharedPages (
459 Resets a network adapter and allocates the transmit and receive buffers
460 required by the network interface; optionally, also requests allocation of
461 additional transmit and receive buffers.
463 @param This The protocol instance pointer.
464 @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer
465 space that the driver should allocate for the
466 network interface. Some network interfaces will not
467 be able to use the extra buffer, and the caller
468 will not know if it is actually being used.
469 @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
470 space that the driver should allocate for the
471 network interface. Some network interfaces will not
472 be able to use the extra buffer, and the caller
473 will not know if it is actually being used.
475 @retval EFI_SUCCESS The network interface was initialized.
476 @retval EFI_NOT_STARTED The network interface has not been started.
477 @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit
479 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
481 @retval EFI_DEVICE_ERROR The command could not be sent to the network
483 @retval EFI_UNSUPPORTED This function is not supported by the network
489 VirtioNetInitialize (
490 IN EFI_SIMPLE_NETWORK_PROTOCOL
*This
,
491 IN UINTN ExtraRxBufferSize OPTIONAL
,
492 IN UINTN ExtraTxBufferSize OPTIONAL
502 return EFI_INVALID_PARAMETER
;
505 if ((ExtraRxBufferSize
> 0) || (ExtraTxBufferSize
> 0)) {
506 return EFI_UNSUPPORTED
;
509 Dev
= VIRTIO_NET_FROM_SNP (This
);
510 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
511 if (Dev
->Snm
.State
!= EfiSimpleNetworkStarted
) {
512 Status
= EFI_NOT_STARTED
;
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().
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.
525 NextDevStat
= VSTAT_ACK
; // step 2 -- acknowledge device presence
526 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
527 if (EFI_ERROR (Status
)) {
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
)) {
538 // Set Page Size - MMIO VirtIo Specific
540 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
541 if (EFI_ERROR (Status
)) {
546 // step 4a -- retrieve features. Note that we're past validating required
547 // features in VirtioNetGetFeatures().
549 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
550 if (EFI_ERROR (Status
)) {
554 ASSERT (Features
& VIRTIO_NET_F_MAC
);
556 Dev
->Snm
.MediaPresentSupported
==
557 !!(Features
& VIRTIO_NET_F_STATUS
)
560 Features
&= VIRTIO_NET_F_MAC
| VIRTIO_NET_F_STATUS
| VIRTIO_F_VERSION_1
|
561 VIRTIO_F_IOMMU_PLATFORM
;
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.
567 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
568 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
569 if (EFI_ERROR (Status
)) {
575 // step 4b, 4c -- allocate and report virtqueues
577 Status
= VirtioNetInitRing (
583 if (EFI_ERROR (Status
)) {
587 Status
= VirtioNetInitRing (
593 if (EFI_ERROR (Status
)) {
598 // step 5 -- keep only the features we want
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
)) {
609 // step 6 -- virtio-net initialization complete
611 NextDevStat
|= VSTAT_DRIVER_OK
;
612 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
613 if (EFI_ERROR (Status
)) {
617 Status
= VirtioNetInitTx (Dev
);
618 if (EFI_ERROR (Status
)) {
625 Status
= VirtioNetInitRx (Dev
);
626 if (EFI_ERROR (Status
)) {
630 Dev
->Snm
.State
= EfiSimpleNetworkInitialized
;
631 gBS
->RestoreTPL (OldTpl
);
635 VirtioNetShutdownTx (Dev
);
638 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
641 VirtioNetUninitRing (Dev
, &Dev
->TxRing
, Dev
->TxRingMap
);
644 VirtioNetUninitRing (Dev
, &Dev
->RxRing
, Dev
->RxRingMap
);
648 // restore device status invariant for the EfiSimpleNetworkStarted state
650 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
653 gBS
->RestoreTPL (OldTpl
);