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 @retval EFI_OUT_OF_RESOURCES Failed to allocate RX destination area.
246 @return Status codes from VIRTIO_CFG_WRITE().
247 @retval EFI_SUCCESS RX setup successful. The device is live and may
248 already be writing to the receive area.
259 UINTN VirtioNetReqSize
;
261 UINT16 RxAlwaysPending
;
267 // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
268 // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
270 VirtioNetReqSize
= (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) ?
271 sizeof (VIRTIO_NET_REQ
) :
272 sizeof (VIRTIO_1_0_NET_REQ
);
275 // For each incoming packet we must supply two descriptors:
276 // - the recipient for the virtio-net request header, plus
277 // - the recipient for the network data (which consists of Ethernet header
278 // and Ethernet payload).
280 RxBufSize
= VirtioNetReqSize
+
281 (Dev
->Snm
.MediaHeaderSize
+ Dev
->Snm
.MaxPacketSize
);
284 // Limit the number of pending RX packets if the queue is big. The division
285 // by two is due to the above "two descriptors per packet" trait.
287 RxAlwaysPending
= (UINT16
) MIN (Dev
->RxRing
.QueueSize
/ 2, VNET_MAX_PENDING
);
289 Dev
->RxBuf
= AllocatePool (RxAlwaysPending
* RxBufSize
);
290 if (Dev
->RxBuf
== NULL
) {
291 return EFI_OUT_OF_RESOURCES
;
295 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
298 Dev
->RxLastUsed
= *Dev
->RxRing
.Used
.Idx
;
299 ASSERT (Dev
->RxLastUsed
== 0);
302 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
303 // the host should not send interrupts, we'll poll in VirtioNetReceive()
304 // and VirtioNetIsPacketAvailable().
306 *Dev
->RxRing
.Avail
.Flags
= (UINT16
) VRING_AVAIL_F_NO_INTERRUPT
;
309 // now set up a separate, two-part descriptor chain for each RX packet, and
310 // link each chain into (from) the available ring as well
314 for (PktIdx
= 0; PktIdx
< RxAlwaysPending
; ++PktIdx
) {
316 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
317 // invisible to the host until we update the Index Field
319 Dev
->RxRing
.Avail
.Ring
[PktIdx
] = DescIdx
;
322 // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
324 Dev
->RxRing
.Desc
[DescIdx
].Addr
= (UINTN
) RxPtr
;
325 Dev
->RxRing
.Desc
[DescIdx
].Len
= (UINT32
) VirtioNetReqSize
;
326 Dev
->RxRing
.Desc
[DescIdx
].Flags
= VRING_DESC_F_WRITE
| VRING_DESC_F_NEXT
;
327 Dev
->RxRing
.Desc
[DescIdx
].Next
= (UINT16
) (DescIdx
+ 1);
328 RxPtr
+= Dev
->RxRing
.Desc
[DescIdx
++].Len
;
330 Dev
->RxRing
.Desc
[DescIdx
].Addr
= (UINTN
) RxPtr
;
331 Dev
->RxRing
.Desc
[DescIdx
].Len
= (UINT32
) (RxBufSize
- VirtioNetReqSize
);
332 Dev
->RxRing
.Desc
[DescIdx
].Flags
= VRING_DESC_F_WRITE
;
333 RxPtr
+= Dev
->RxRing
.Desc
[DescIdx
++].Len
;
337 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
340 *Dev
->RxRing
.Avail
.Idx
= RxAlwaysPending
;
343 // At this point reception may already be running. In order to make it sure,
344 // kick the hypervisor. If we fail to kick it, we must first abort reception
345 // before tearing down anything, because reception may have been already
346 // running even without the kick.
348 // virtio-0.9.5, 2.4.1.4 Notifying the Device
351 Status
= Dev
->VirtIo
->SetQueueNotify (Dev
->VirtIo
, VIRTIO_NET_Q_RX
);
352 if (EFI_ERROR (Status
)) {
353 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
354 FreePool (Dev
->RxBuf
);
362 Resets a network adapter and allocates the transmit and receive buffers
363 required by the network interface; optionally, also requests allocation of
364 additional transmit and receive buffers.
366 @param This The protocol instance pointer.
367 @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer
368 space that the driver should allocate for the
369 network interface. Some network interfaces will not
370 be able to use the extra buffer, and the caller
371 will not know if it is actually being used.
372 @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
373 space that the driver should allocate for the
374 network interface. Some network interfaces will not
375 be able to use the extra buffer, and the caller
376 will not know if it is actually being used.
378 @retval EFI_SUCCESS The network interface was initialized.
379 @retval EFI_NOT_STARTED The network interface has not been started.
380 @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit
382 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
384 @retval EFI_DEVICE_ERROR The command could not be sent to the network
386 @retval EFI_UNSUPPORTED This function is not supported by the network
393 VirtioNetInitialize (
394 IN EFI_SIMPLE_NETWORK_PROTOCOL
*This
,
395 IN UINTN ExtraRxBufferSize OPTIONAL
,
396 IN UINTN ExtraTxBufferSize OPTIONAL
406 return EFI_INVALID_PARAMETER
;
408 if (ExtraRxBufferSize
> 0 || ExtraTxBufferSize
> 0) {
409 return EFI_UNSUPPORTED
;
412 Dev
= VIRTIO_NET_FROM_SNP (This
);
413 OldTpl
= gBS
->RaiseTPL (TPL_CALLBACK
);
414 if (Dev
->Snm
.State
!= EfiSimpleNetworkStarted
) {
415 Status
= EFI_NOT_STARTED
;
420 // In the EfiSimpleNetworkStarted state the virtio-net device has status
421 // value 0 (= reset) -- see the state diagram, the full call chain to
422 // the end of VirtioNetGetFeatures() (considering we're here now),
423 // the DeviceFailed label below, and VirtioNetShutdown().
425 // Accordingly, the below is a subsequence of the steps found in the
426 // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
428 NextDevStat
= VSTAT_ACK
; // step 2 -- acknowledge device presence
429 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
430 if (EFI_ERROR (Status
)) {
434 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
435 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
436 if (EFI_ERROR (Status
)) {
441 // Set Page Size - MMIO VirtIo Specific
443 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
444 if (EFI_ERROR (Status
)) {
449 // step 4a -- retrieve features. Note that we're past validating required
450 // features in VirtioNetGetFeatures().
452 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
453 if (EFI_ERROR (Status
)) {
457 ASSERT (Features
& VIRTIO_NET_F_MAC
);
458 ASSERT (Dev
->Snm
.MediaPresentSupported
==
459 !!(Features
& VIRTIO_NET_F_STATUS
));
461 Features
&= VIRTIO_NET_F_MAC
| VIRTIO_NET_F_STATUS
| VIRTIO_F_VERSION_1
;
464 // In virtio-1.0, feature negotiation is expected to complete before queue
465 // discovery, and the device can also reject the selected set of features.
467 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
468 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
469 if (EFI_ERROR (Status
)) {
475 // step 4b, 4c -- allocate and report virtqueues
477 Status
= VirtioNetInitRing (
483 if (EFI_ERROR (Status
)) {
487 Status
= VirtioNetInitRing (
493 if (EFI_ERROR (Status
)) {
498 // step 5 -- keep only the features we want
500 if (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
501 Features
&= ~(UINT64
)VIRTIO_F_VERSION_1
;
502 Status
= Dev
->VirtIo
->SetGuestFeatures (Dev
->VirtIo
, Features
);
503 if (EFI_ERROR (Status
)) {
509 // step 6 -- virtio-net initialization complete
511 NextDevStat
|= VSTAT_DRIVER_OK
;
512 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
513 if (EFI_ERROR (Status
)) {
517 Status
= VirtioNetInitTx (Dev
);
518 if (EFI_ERROR (Status
)) {
525 Status
= VirtioNetInitRx (Dev
);
526 if (EFI_ERROR (Status
)) {
530 Dev
->Snm
.State
= EfiSimpleNetworkInitialized
;
531 gBS
->RestoreTPL (OldTpl
);
535 VirtioNetShutdownTx (Dev
);
538 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
541 VirtioNetUninitRing (Dev
, &Dev
->TxRing
, Dev
->TxRingMap
);
544 VirtioNetUninitRing (Dev
, &Dev
->RxRing
, Dev
->RxRingMap
);
548 // restore device status invariant for the EfiSimpleNetworkStarted state
550 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
553 gBS
->RestoreTPL (OldTpl
);