]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg/VirtioNetDxe: map VRINGs using VirtioRingMap()
[mirror_edk2.git] / OvmfPkg / VirtioNetDxe / SnpInitialize.c
1 /** @file
2
3 Implementation of the SNP.Initialize() function and its private helpers if
4 any.
5
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>
9
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
14
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.
17
18 **/
19
20 #include <Library/BaseLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23
24 #include "VirtioNet.h"
25
26 /**
27 Initialize a virtio ring for a specific transfer direction of the virtio-net
28 device.
29
30 This function may only be called by VirtioNetInitialize().
31
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
35 the network device.
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()
39
40 @retval EFI_UNSUPPORTED The queue size reported by the virtio-net device is
41 too small.
42 @return Status codes from VIRTIO_CFG_WRITE(),
43 VIRTIO_CFG_READ(), VirtioRingInit() and
44 VirtioRingMap().
45 @retval EFI_SUCCESS Ring initialized.
46 */
47
48 STATIC
49 EFI_STATUS
50 EFIAPI
51 VirtioNetInitRing (
52 IN OUT VNET_DEV *Dev,
53 IN UINT16 Selector,
54 OUT VRING *Ring,
55 OUT VOID **Mapping
56 )
57 {
58 EFI_STATUS Status;
59 UINT16 QueueSize;
60 UINT64 RingBaseShift;
61 VOID *MapInfo;
62
63 //
64 // step 4b -- allocate selected queue
65 //
66 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
67 if (EFI_ERROR (Status)) {
68 return Status;
69 }
70 Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
71 if (EFI_ERROR (Status)) {
72 return Status;
73 }
74
75 //
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
78 //
79 if (QueueSize < 2) {
80 return EFI_UNSUPPORTED;
81 }
82 Status = VirtioRingInit (Dev->VirtIo, QueueSize, Ring);
83 if (EFI_ERROR (Status)) {
84 return Status;
85 }
86
87 //
88 // If anything fails from here on, we must release the ring resources.
89 //
90 Status = VirtioRingMap (Dev->VirtIo, Ring, &RingBaseShift, &MapInfo);
91 if (EFI_ERROR (Status)) {
92 goto ReleaseQueue;
93 }
94
95 //
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.
98 //
99 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
100 if (EFI_ERROR (Status)) {
101 goto UnmapQueue;
102 }
103
104 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
105 if (EFI_ERROR (Status)) {
106 goto UnmapQueue;
107 }
108
109 //
110 // step 4c -- report GPFN (guest-physical frame number) of queue
111 //
112 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring, RingBaseShift);
113 if (EFI_ERROR (Status)) {
114 goto UnmapQueue;
115 }
116
117 *Mapping = MapInfo;
118
119 return EFI_SUCCESS;
120
121 UnmapQueue:
122 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, MapInfo);
123
124 ReleaseQueue:
125 VirtioRingUninit (Dev->VirtIo, Ring);
126
127 return Status;
128 }
129
130
131 /**
132 Set up static scaffolding for the VirtioNetTransmit() and
133 VirtioNetGetStatus() SNP methods.
134
135 This function may only be called by VirtioNetInitialize().
136
137 The structures laid out and resources configured include:
138 - fully populate the TX queue with a static pattern of virtio descriptor
139 chains,
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
142 pending TX packets,
143 - select polling over TX interrupt.
144
145 @param[in,out] Dev The VNET_DEV driver instance about to enter the
146 EfiSimpleNetworkInitialized state.
147
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.
151 */
152
153 STATIC
154 EFI_STATUS
155 EFIAPI
156 VirtioNetInitTx (
157 IN OUT VNET_DEV *Dev
158 )
159 {
160 UINTN TxSharedReqSize;
161 UINTN PktIdx;
162
163 Dev->TxMaxPending = (UINT16) MIN (Dev->TxRing.QueueSize / 2,
164 VNET_MAX_PENDING);
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;
170 }
171
172 //
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.
175 //
176 TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
177 sizeof Dev->TxSharedReq.V0_9_5 :
178 sizeof Dev->TxSharedReq;
179
180 for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
181 UINT16 DescIdx;
182
183 DescIdx = (UINT16) (2 * PktIdx);
184 Dev->TxFreeStack[PktIdx] = DescIdx;
185
186 //
187 // For each possibly pending packet, lay out the descriptor for the common
188 // (unmodified by the host) virtio-net request header.
189 //
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);
194
195 //
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.
198 //
199 Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
200 }
201
202 //
203 // virtio-0.9.5, Appendix C, Packet Transmission
204 //
205 Dev->TxSharedReq.V0_9_5.Flags = 0;
206 Dev->TxSharedReq.V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
207
208 //
209 // For VirtIo 1.0 only -- the field exists, but it is unused
210 //
211 Dev->TxSharedReq.NumBuffers = 0;
212
213 //
214 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
215 //
216 MemoryFence ();
217 Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
218 ASSERT (Dev->TxLastUsed == 0);
219
220 //
221 // want no interrupt when a transmit completes
222 //
223 *Dev->TxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
224
225 return EFI_SUCCESS;
226 }
227
228
229 /**
230 Set up static scaffolding for the VirtioNetReceive() SNP method and enable
231 live device operation.
232
233 This function may only be called as VirtioNetInitialize()'s final step.
234
235 The structures laid out and resources configured include:
236 - destination area for the host to write virtio-net request headers and
237 packet data into,
238 - select polling over RX interrupt,
239 - fully populate the RX queue with a static pattern of virtio descriptor
240 chains.
241
242 @param[in,out] Dev The VNET_DEV driver instance about to enter the
243 EfiSimpleNetworkInitialized state.
244
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.
249 */
250
251 STATIC
252 EFI_STATUS
253 EFIAPI
254 VirtioNetInitRx (
255 IN OUT VNET_DEV *Dev
256 )
257 {
258 EFI_STATUS Status;
259 UINTN VirtioNetReqSize;
260 UINTN RxBufSize;
261 UINT16 RxAlwaysPending;
262 UINTN PktIdx;
263 UINT16 DescIdx;
264 UINT8 *RxPtr;
265
266 //
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.
269 //
270 VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
271 sizeof (VIRTIO_NET_REQ) :
272 sizeof (VIRTIO_1_0_NET_REQ);
273
274 //
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).
279 //
280 RxBufSize = VirtioNetReqSize +
281 (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
282
283 //
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.
286 //
287 RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
288
289 Dev->RxBuf = AllocatePool (RxAlwaysPending * RxBufSize);
290 if (Dev->RxBuf == NULL) {
291 return EFI_OUT_OF_RESOURCES;
292 }
293
294 //
295 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
296 //
297 MemoryFence ();
298 Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
299 ASSERT (Dev->RxLastUsed == 0);
300
301 //
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().
305 //
306 *Dev->RxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
307
308 //
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
311 //
312 DescIdx = 0;
313 RxPtr = Dev->RxBuf;
314 for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
315 //
316 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
317 // invisible to the host until we update the Index Field
318 //
319 Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
320
321 //
322 // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
323 //
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;
329
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;
334 }
335
336 //
337 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
338 //
339 MemoryFence ();
340 *Dev->RxRing.Avail.Idx = RxAlwaysPending;
341
342 //
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.
347 //
348 // virtio-0.9.5, 2.4.1.4 Notifying the Device
349 //
350 MemoryFence ();
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);
355 }
356
357 return Status;
358 }
359
360
361 /**
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.
365
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.
377
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
381 and receive buffers.
382 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
383 unsupported value.
384 @retval EFI_DEVICE_ERROR The command could not be sent to the network
385 interface.
386 @retval EFI_UNSUPPORTED This function is not supported by the network
387 interface.
388
389 **/
390
391 EFI_STATUS
392 EFIAPI
393 VirtioNetInitialize (
394 IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
395 IN UINTN ExtraRxBufferSize OPTIONAL,
396 IN UINTN ExtraTxBufferSize OPTIONAL
397 )
398 {
399 VNET_DEV *Dev;
400 EFI_TPL OldTpl;
401 EFI_STATUS Status;
402 UINT8 NextDevStat;
403 UINT64 Features;
404
405 if (This == NULL) {
406 return EFI_INVALID_PARAMETER;
407 }
408 if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
409 return EFI_UNSUPPORTED;
410 }
411
412 Dev = VIRTIO_NET_FROM_SNP (This);
413 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
414 if (Dev->Snm.State != EfiSimpleNetworkStarted) {
415 Status = EFI_NOT_STARTED;
416 goto InitFailed;
417 }
418
419 //
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().
424 //
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.
427 //
428 NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
429 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
430 if (EFI_ERROR (Status)) {
431 goto InitFailed;
432 }
433
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)) {
437 goto DeviceFailed;
438 }
439
440 //
441 // Set Page Size - MMIO VirtIo Specific
442 //
443 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
444 if (EFI_ERROR (Status)) {
445 goto DeviceFailed;
446 }
447
448 //
449 // step 4a -- retrieve features. Note that we're past validating required
450 // features in VirtioNetGetFeatures().
451 //
452 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
453 if (EFI_ERROR (Status)) {
454 goto DeviceFailed;
455 }
456
457 ASSERT (Features & VIRTIO_NET_F_MAC);
458 ASSERT (Dev->Snm.MediaPresentSupported ==
459 !!(Features & VIRTIO_NET_F_STATUS));
460
461 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1;
462
463 //
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.
466 //
467 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
468 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
469 if (EFI_ERROR (Status)) {
470 goto DeviceFailed;
471 }
472 }
473
474 //
475 // step 4b, 4c -- allocate and report virtqueues
476 //
477 Status = VirtioNetInitRing (
478 Dev,
479 VIRTIO_NET_Q_RX,
480 &Dev->RxRing,
481 &Dev->RxRingMap
482 );
483 if (EFI_ERROR (Status)) {
484 goto DeviceFailed;
485 }
486
487 Status = VirtioNetInitRing (
488 Dev,
489 VIRTIO_NET_Q_TX,
490 &Dev->TxRing,
491 &Dev->TxRingMap
492 );
493 if (EFI_ERROR (Status)) {
494 goto ReleaseRxRing;
495 }
496
497 //
498 // step 5 -- keep only the features we want
499 //
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)) {
504 goto ReleaseTxRing;
505 }
506 }
507
508 //
509 // step 6 -- virtio-net initialization complete
510 //
511 NextDevStat |= VSTAT_DRIVER_OK;
512 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
513 if (EFI_ERROR (Status)) {
514 goto ReleaseTxRing;
515 }
516
517 Status = VirtioNetInitTx (Dev);
518 if (EFI_ERROR (Status)) {
519 goto AbortDevice;
520 }
521
522 //
523 // start receiving
524 //
525 Status = VirtioNetInitRx (Dev);
526 if (EFI_ERROR (Status)) {
527 goto ReleaseTxAux;
528 }
529
530 Dev->Snm.State = EfiSimpleNetworkInitialized;
531 gBS->RestoreTPL (OldTpl);
532 return EFI_SUCCESS;
533
534 ReleaseTxAux:
535 VirtioNetShutdownTx (Dev);
536
537 AbortDevice:
538 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
539
540 ReleaseTxRing:
541 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
542
543 ReleaseRxRing:
544 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
545
546 DeviceFailed:
547 //
548 // restore device status invariant for the EfiSimpleNetworkStarted state
549 //
550 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
551
552 InitFailed:
553 gBS->RestoreTPL (OldTpl);
554 return Status;
555 }