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 This program and the accompanying materials are licensed and made available
11 under the terms and conditions of the BSD License which accompanies this
12 distribution. The full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
16 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 #include <Library/BaseLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
24 #include "VirtioNet.h"
27 Initialize a virtio ring for a specific transfer direction of the virtio-net
30 This function may only be called by VirtioNetInitialize().
32 @param[in,out] Dev The VNET_DEV driver instance about to enter the
33 EfiSimpleNetworkInitialized state.
34 @param[in] Selector Identifies the transfer direction (virtio queue) of
36 @param[out] Ring The virtio-ring inside the VNET_DEV structure,
37 corresponding to Selector.
38 @param[out] Mapping A resulting token to pass to VirtioNetUninitRing()
40 @retval EFI_UNSUPPORTED The queue size reported by the virtio-net device is
42 @return Status codes from VIRTIO_CFG_WRITE(),
43 VIRTIO_CFG_READ(), VirtioRingInit() and
45 @retval EFI_SUCCESS Ring initialized.
64 // step 4b -- allocate selected queue
66 Status
= Dev
->VirtIo
->SetQueueSel (Dev
->VirtIo
, Selector
);
67 if (EFI_ERROR (Status
)) {
70 Status
= Dev
->VirtIo
->GetQueueNumMax (Dev
->VirtIo
, &QueueSize
);
71 if (EFI_ERROR (Status
)) {
76 // For each packet (RX and TX alike), we need two descriptors:
77 // one for the virtio-net request header, and another one for the data
80 return EFI_UNSUPPORTED
;
82 Status
= VirtioRingInit (Dev
->VirtIo
, QueueSize
, Ring
);
83 if (EFI_ERROR (Status
)) {
88 // If anything fails from here on, we must release the ring resources.
90 Status
= VirtioRingMap (Dev
->VirtIo
, Ring
, &RingBaseShift
, &MapInfo
);
91 if (EFI_ERROR (Status
)) {
96 // Additional steps for MMIO: align the queue appropriately, and set the
97 // size. If anything fails from here on, we must unmap the ring resources.
99 Status
= Dev
->VirtIo
->SetQueueNum (Dev
->VirtIo
, QueueSize
);
100 if (EFI_ERROR (Status
)) {
104 Status
= Dev
->VirtIo
->SetQueueAlign (Dev
->VirtIo
, EFI_PAGE_SIZE
);
105 if (EFI_ERROR (Status
)) {
110 // step 4c -- report GPFN (guest-physical frame number) of queue
112 Status
= Dev
->VirtIo
->SetQueueAddress (Dev
->VirtIo
, Ring
, RingBaseShift
);
113 if (EFI_ERROR (Status
)) {
122 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, MapInfo
);
125 VirtioRingUninit (Dev
->VirtIo
, Ring
);
132 Set up static scaffolding for the VirtioNetTransmit() and
133 VirtioNetGetStatus() SNP methods.
135 This function may only be called by VirtioNetInitialize().
137 The structures laid out and resources configured include:
138 - fully populate the TX queue with a static pattern of virtio descriptor
140 - tracking of heads of free descriptor chains from the above,
141 - one common virtio-net request header (never modified by the host) for all
143 - select polling over TX interrupt.
145 @param[in,out] Dev The VNET_DEV driver instance about to enter the
146 EfiSimpleNetworkInitialized state.
148 @retval EFI_OUT_OF_RESOURCES Failed to allocate the stack to track the heads
149 of free descriptor chains.
150 @retval EFI_SUCCESS TX setup successful.
160 UINTN TxSharedReqSize
;
163 Dev
->TxMaxPending
= (UINT16
) MIN (Dev
->TxRing
.QueueSize
/ 2,
165 Dev
->TxCurPending
= 0;
166 Dev
->TxFreeStack
= AllocatePool (Dev
->TxMaxPending
*
167 sizeof *Dev
->TxFreeStack
);
168 if (Dev
->TxFreeStack
== NULL
) {
169 return EFI_OUT_OF_RESOURCES
;
173 // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
174 // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
176 TxSharedReqSize
= (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) ?
177 sizeof Dev
->TxSharedReq
.V0_9_5
:
178 sizeof Dev
->TxSharedReq
;
180 for (PktIdx
= 0; PktIdx
< Dev
->TxMaxPending
; ++PktIdx
) {
183 DescIdx
= (UINT16
) (2 * PktIdx
);
184 Dev
->TxFreeStack
[PktIdx
] = DescIdx
;
187 // For each possibly pending packet, lay out the descriptor for the common
188 // (unmodified by the host) virtio-net request header.
190 Dev
->TxRing
.Desc
[DescIdx
].Addr
= (UINTN
) &Dev
->TxSharedReq
;
191 Dev
->TxRing
.Desc
[DescIdx
].Len
= (UINT32
) TxSharedReqSize
;
192 Dev
->TxRing
.Desc
[DescIdx
].Flags
= VRING_DESC_F_NEXT
;
193 Dev
->TxRing
.Desc
[DescIdx
].Next
= (UINT16
) (DescIdx
+ 1);
196 // The second descriptor of each pending TX packet is updated on the fly,
197 // but it always terminates the descriptor chain of the packet.
199 Dev
->TxRing
.Desc
[DescIdx
+ 1].Flags
= 0;
203 // virtio-0.9.5, Appendix C, Packet Transmission
205 Dev
->TxSharedReq
.V0_9_5
.Flags
= 0;
206 Dev
->TxSharedReq
.V0_9_5
.GsoType
= VIRTIO_NET_HDR_GSO_NONE
;
209 // For VirtIo 1.0 only -- the field exists, but it is unused
211 Dev
->TxSharedReq
.NumBuffers
= 0;
214 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
217 Dev
->TxLastUsed
= *Dev
->TxRing
.Used
.Idx
;
218 ASSERT (Dev
->TxLastUsed
== 0);
221 // want no interrupt when a transmit completes
223 *Dev
->TxRing
.Avail
.Flags
= (UINT16
) VRING_AVAIL_F_NO_INTERRUPT
;
230 Set up static scaffolding for the VirtioNetReceive() SNP method and enable
231 live device operation.
233 This function may only be called as VirtioNetInitialize()'s final step.
235 The structures laid out and resources configured include:
236 - destination area for the host to write virtio-net request headers and
238 - select polling over RX interrupt,
239 - fully populate the RX queue with a static pattern of virtio descriptor
242 @param[in,out] Dev The VNET_DEV driver instance about to enter the
243 EfiSimpleNetworkInitialized state.
245 @return Status codes from VIRTIO_CFG_WRITE() or
246 VIRTIO_DEVICE_PROTOCOL.AllocateSharedPages or
247 VirtioMapAllBytesInSharedBuffer().
248 @retval EFI_SUCCESS RX setup successful. The device is live and may
249 already be writing to the receive area.
260 UINTN VirtioNetReqSize
;
262 UINT16 RxAlwaysPending
;
266 EFI_PHYSICAL_ADDRESS RxBufDeviceAddress
;
270 // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
271 // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
273 VirtioNetReqSize
= (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) ?
274 sizeof (VIRTIO_NET_REQ
) :
275 sizeof (VIRTIO_1_0_NET_REQ
);
278 // For each incoming packet we must supply two descriptors:
279 // - the recipient for the virtio-net request header, plus
280 // - the recipient for the network data (which consists of Ethernet header
281 // and Ethernet payload).
283 RxBufSize
= VirtioNetReqSize
+
284 (Dev
->Snm
.MediaHeaderSize
+ Dev
->Snm
.MaxPacketSize
);
287 // Limit the number of pending RX packets if the queue is big. The division
288 // by two is due to the above "two descriptors per packet" trait.
290 RxAlwaysPending
= (UINT16
) MIN (Dev
->RxRing
.QueueSize
/ 2, VNET_MAX_PENDING
);
293 // The RxBuf is shared between guest and hypervisor, use
294 // AllocateSharedPages() to allocate this memory region and map it with
295 // BusMasterCommonBuffer so that it can be accessed by both guest and
298 NumBytes
= RxAlwaysPending
* RxBufSize
;
299 Dev
->RxBufNrPages
= EFI_SIZE_TO_PAGES (NumBytes
);
300 Status
= Dev
->VirtIo
->AllocateSharedPages (
305 if (EFI_ERROR (Status
)) {
309 ZeroMem (RxBuffer
, NumBytes
);
311 Status
= VirtioMapAllBytesInSharedBuffer (
313 VirtioOperationBusMasterCommonBuffer
,
316 &Dev
->RxBufDeviceBase
,
319 if (EFI_ERROR (Status
)) {
320 goto FreeSharedBuffer
;
323 Dev
->RxBuf
= RxBuffer
;
326 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
329 Dev
->RxLastUsed
= *Dev
->RxRing
.Used
.Idx
;
330 ASSERT (Dev
->RxLastUsed
== 0);
333 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
334 // the host should not send interrupts, we'll poll in VirtioNetReceive()
335 // and VirtioNetIsPacketAvailable().
337 *Dev
->RxRing
.Avail
.Flags
= (UINT16
) VRING_AVAIL_F_NO_INTERRUPT
;
340 // now set up a separate, two-part descriptor chain for each RX packet, and
341 // link each chain into (from) the available ring as well
344 RxBufDeviceAddress
= Dev
->RxBufDeviceBase
;
345 for (PktIdx
= 0; PktIdx
< RxAlwaysPending
; ++PktIdx
) {
347 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
348 // invisible to the host until we update the Index Field
350 Dev
->RxRing
.Avail
.Ring
[PktIdx
] = DescIdx
;
353 // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
355 Dev
->RxRing
.Desc
[DescIdx
].Addr
= RxBufDeviceAddress
;
356 Dev
->RxRing
.Desc
[DescIdx
].Len
= (UINT32
) VirtioNetReqSize
;
357 Dev
->RxRing
.Desc
[DescIdx
].Flags
= VRING_DESC_F_WRITE
| VRING_DESC_F_NEXT
;
358 Dev
->RxRing
.Desc
[DescIdx
].Next
= (UINT16
) (DescIdx
+ 1);
359 RxBufDeviceAddress
+= Dev
->RxRing
.Desc
[DescIdx
++].Len
;
361 Dev
->RxRing
.Desc
[DescIdx
].Addr
= RxBufDeviceAddress
;
362 Dev
->RxRing
.Desc
[DescIdx
].Len
= (UINT32
) (RxBufSize
- VirtioNetReqSize
);
363 Dev
->RxRing
.Desc
[DescIdx
].Flags
= VRING_DESC_F_WRITE
;
364 RxBufDeviceAddress
+= Dev
->RxRing
.Desc
[DescIdx
++].Len
;
368 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
371 *Dev
->RxRing
.Avail
.Idx
= RxAlwaysPending
;
374 // At this point reception may already be running. In order to make it sure,
375 // kick the hypervisor. If we fail to kick it, we must first abort reception
376 // before tearing down anything, because reception may have been already
377 // running even without the kick.
379 // virtio-0.9.5, 2.4.1.4 Notifying the Device
382 Status
= Dev
->VirtIo
->SetQueueNotify (Dev
->VirtIo
, VIRTIO_NET_Q_RX
);
383 if (EFI_ERROR (Status
)) {
384 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
385 goto UnmapSharedBuffer
;
391 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RxBufMap
);
394 Dev
->VirtIo
->FreeSharedPages (
404 Resets a network adapter and allocates the transmit and receive buffers
405 required by the network interface; optionally, also requests allocation of
406 additional transmit and receive buffers.
408 @param This The protocol instance pointer.
409 @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer
410 space that the driver should allocate for the
411 network interface. Some network interfaces will not
412 be able to use the extra buffer, and the caller
413 will not know if it is actually being used.
414 @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
415 space that the driver should allocate for the
416 network interface. Some network interfaces will not
417 be able to use the extra buffer, and the caller
418 will not know if it is actually being used.
420 @retval EFI_SUCCESS The network interface was initialized.
421 @retval EFI_NOT_STARTED The network interface has not been started.
422 @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit
424 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
426 @retval EFI_DEVICE_ERROR The command could not be sent to the network
428 @retval EFI_UNSUPPORTED This function is not supported by the network
435 VirtioNetInitialize (
436 IN EFI_SIMPLE_NETWORK_PROTOCOL
*This
,
437 IN UINTN ExtraRxBufferSize OPTIONAL
,
438 IN UINTN ExtraTxBufferSize OPTIONAL
448 return EFI_INVALID_PARAMETER
;
450 if (ExtraRxBufferSize
> 0 || ExtraTxBufferSize
> 0) {
451 return EFI_UNSUPPORTED
;
454 Dev
= VIRTIO_NET_FROM_SNP (This
);
455 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
456 if (Dev
->Snm
.State
!= EfiSimpleNetworkStarted
) {
457 Status
= EFI_NOT_STARTED
;
462 // In the EfiSimpleNetworkStarted state the virtio-net device has status
463 // value 0 (= reset) -- see the state diagram, the full call chain to
464 // the end of VirtioNetGetFeatures() (considering we're here now),
465 // the DeviceFailed label below, and VirtioNetShutdown().
467 // Accordingly, the below is a subsequence of the steps found in the
468 // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
470 NextDevStat
= VSTAT_ACK
; // step 2 -- acknowledge device presence
471 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
472 if (EFI_ERROR (Status
)) {
476 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
477 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
478 if (EFI_ERROR (Status
)) {
483 // Set Page Size - MMIO VirtIo Specific
485 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
486 if (EFI_ERROR (Status
)) {
491 // step 4a -- retrieve features. Note that we're past validating required
492 // features in VirtioNetGetFeatures().
494 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
495 if (EFI_ERROR (Status
)) {
499 ASSERT (Features
& VIRTIO_NET_F_MAC
);
500 ASSERT (Dev
->Snm
.MediaPresentSupported
==
501 !!(Features
& VIRTIO_NET_F_STATUS
));
503 Features
&= VIRTIO_NET_F_MAC
| VIRTIO_NET_F_STATUS
| VIRTIO_F_VERSION_1
;
506 // In virtio-1.0, feature negotiation is expected to complete before queue
507 // discovery, and the device can also reject the selected set of features.
509 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
510 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
511 if (EFI_ERROR (Status
)) {
517 // step 4b, 4c -- allocate and report virtqueues
519 Status
= VirtioNetInitRing (
525 if (EFI_ERROR (Status
)) {
529 Status
= VirtioNetInitRing (
535 if (EFI_ERROR (Status
)) {
540 // step 5 -- keep only the features we want
542 if (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
543 Features
&= ~(UINT64
)VIRTIO_F_VERSION_1
;
544 Status
= Dev
->VirtIo
->SetGuestFeatures (Dev
->VirtIo
, Features
);
545 if (EFI_ERROR (Status
)) {
551 // step 6 -- virtio-net initialization complete
553 NextDevStat
|= VSTAT_DRIVER_OK
;
554 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
555 if (EFI_ERROR (Status
)) {
559 Status
= VirtioNetInitTx (Dev
);
560 if (EFI_ERROR (Status
)) {
567 Status
= VirtioNetInitRx (Dev
);
568 if (EFI_ERROR (Status
)) {
572 Dev
->Snm
.State
= EfiSimpleNetworkInitialized
;
573 gBS
->RestoreTPL (OldTpl
);
577 VirtioNetShutdownTx (Dev
);
580 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
583 VirtioNetUninitRing (Dev
, &Dev
->TxRing
, Dev
->TxRingMap
);
586 VirtioNetUninitRing (Dev
, &Dev
->RxRing
, Dev
->RxRingMap
);
590 // restore device status invariant for the EfiSimpleNetworkStarted state
592 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
595 gBS
->RestoreTPL (OldTpl
);