]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioNetDxe/SnpInitialize.c
OvmfPkg/VirtioNetDxe: alloc RxBuf using AllocateSharedPages()
[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 @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.
250 */
251
252 STATIC
253 EFI_STATUS
254 EFIAPI
255 VirtioNetInitRx (
256 IN OUT VNET_DEV *Dev
257 )
258 {
259 EFI_STATUS Status;
260 UINTN VirtioNetReqSize;
261 UINTN RxBufSize;
262 UINT16 RxAlwaysPending;
263 UINTN PktIdx;
264 UINT16 DescIdx;
265 UINTN NumBytes;
266 EFI_PHYSICAL_ADDRESS RxBufDeviceAddress;
267 VOID *RxBuffer;
268
269 //
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.
272 //
273 VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
274 sizeof (VIRTIO_NET_REQ) :
275 sizeof (VIRTIO_1_0_NET_REQ);
276
277 //
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).
282 //
283 RxBufSize = VirtioNetReqSize +
284 (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
285
286 //
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.
289 //
290 RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
291
292 //
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
296 // hypervisor.
297 //
298 NumBytes = RxAlwaysPending * RxBufSize;
299 Dev->RxBufNrPages = EFI_SIZE_TO_PAGES (NumBytes);
300 Status = Dev->VirtIo->AllocateSharedPages (
301 Dev->VirtIo,
302 Dev->RxBufNrPages,
303 &RxBuffer
304 );
305 if (EFI_ERROR (Status)) {
306 return Status;
307 }
308
309 ZeroMem (RxBuffer, NumBytes);
310
311 Status = VirtioMapAllBytesInSharedBuffer (
312 Dev->VirtIo,
313 VirtioOperationBusMasterCommonBuffer,
314 RxBuffer,
315 NumBytes,
316 &Dev->RxBufDeviceBase,
317 &Dev->RxBufMap
318 );
319 if (EFI_ERROR (Status)) {
320 goto FreeSharedBuffer;
321 }
322
323 Dev->RxBuf = RxBuffer;
324
325 //
326 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
327 //
328 MemoryFence ();
329 Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
330 ASSERT (Dev->RxLastUsed == 0);
331
332 //
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().
336 //
337 *Dev->RxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
338
339 //
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
342 //
343 DescIdx = 0;
344 RxBufDeviceAddress = Dev->RxBufDeviceBase;
345 for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
346 //
347 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
348 // invisible to the host until we update the Index Field
349 //
350 Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
351
352 //
353 // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
354 //
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;
360
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;
365 }
366
367 //
368 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
369 //
370 MemoryFence ();
371 *Dev->RxRing.Avail.Idx = RxAlwaysPending;
372
373 //
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.
378 //
379 // virtio-0.9.5, 2.4.1.4 Notifying the Device
380 //
381 MemoryFence ();
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;
386 }
387
388 return Status;
389
390 UnmapSharedBuffer:
391 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
392
393 FreeSharedBuffer:
394 Dev->VirtIo->FreeSharedPages (
395 Dev->VirtIo,
396 Dev->RxBufNrPages,
397 RxBuffer
398 );
399 return Status;
400 }
401
402
403 /**
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.
407
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.
419
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
423 and receive buffers.
424 @retval EFI_INVALID_PARAMETER One or more of the parameters has an
425 unsupported value.
426 @retval EFI_DEVICE_ERROR The command could not be sent to the network
427 interface.
428 @retval EFI_UNSUPPORTED This function is not supported by the network
429 interface.
430
431 **/
432
433 EFI_STATUS
434 EFIAPI
435 VirtioNetInitialize (
436 IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
437 IN UINTN ExtraRxBufferSize OPTIONAL,
438 IN UINTN ExtraTxBufferSize OPTIONAL
439 )
440 {
441 VNET_DEV *Dev;
442 EFI_TPL OldTpl;
443 EFI_STATUS Status;
444 UINT8 NextDevStat;
445 UINT64 Features;
446
447 if (This == NULL) {
448 return EFI_INVALID_PARAMETER;
449 }
450 if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
451 return EFI_UNSUPPORTED;
452 }
453
454 Dev = VIRTIO_NET_FROM_SNP (This);
455 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
456 if (Dev->Snm.State != EfiSimpleNetworkStarted) {
457 Status = EFI_NOT_STARTED;
458 goto InitFailed;
459 }
460
461 //
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().
466 //
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.
469 //
470 NextDevStat = VSTAT_ACK; // step 2 -- acknowledge device presence
471 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
472 if (EFI_ERROR (Status)) {
473 goto InitFailed;
474 }
475
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)) {
479 goto DeviceFailed;
480 }
481
482 //
483 // Set Page Size - MMIO VirtIo Specific
484 //
485 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
486 if (EFI_ERROR (Status)) {
487 goto DeviceFailed;
488 }
489
490 //
491 // step 4a -- retrieve features. Note that we're past validating required
492 // features in VirtioNetGetFeatures().
493 //
494 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
495 if (EFI_ERROR (Status)) {
496 goto DeviceFailed;
497 }
498
499 ASSERT (Features & VIRTIO_NET_F_MAC);
500 ASSERT (Dev->Snm.MediaPresentSupported ==
501 !!(Features & VIRTIO_NET_F_STATUS));
502
503 Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1;
504
505 //
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.
508 //
509 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
510 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
511 if (EFI_ERROR (Status)) {
512 goto DeviceFailed;
513 }
514 }
515
516 //
517 // step 4b, 4c -- allocate and report virtqueues
518 //
519 Status = VirtioNetInitRing (
520 Dev,
521 VIRTIO_NET_Q_RX,
522 &Dev->RxRing,
523 &Dev->RxRingMap
524 );
525 if (EFI_ERROR (Status)) {
526 goto DeviceFailed;
527 }
528
529 Status = VirtioNetInitRing (
530 Dev,
531 VIRTIO_NET_Q_TX,
532 &Dev->TxRing,
533 &Dev->TxRingMap
534 );
535 if (EFI_ERROR (Status)) {
536 goto ReleaseRxRing;
537 }
538
539 //
540 // step 5 -- keep only the features we want
541 //
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)) {
546 goto ReleaseTxRing;
547 }
548 }
549
550 //
551 // step 6 -- virtio-net initialization complete
552 //
553 NextDevStat |= VSTAT_DRIVER_OK;
554 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
555 if (EFI_ERROR (Status)) {
556 goto ReleaseTxRing;
557 }
558
559 Status = VirtioNetInitTx (Dev);
560 if (EFI_ERROR (Status)) {
561 goto AbortDevice;
562 }
563
564 //
565 // start receiving
566 //
567 Status = VirtioNetInitRx (Dev);
568 if (EFI_ERROR (Status)) {
569 goto ReleaseTxAux;
570 }
571
572 Dev->Snm.State = EfiSimpleNetworkInitialized;
573 gBS->RestoreTPL (OldTpl);
574 return EFI_SUCCESS;
575
576 ReleaseTxAux:
577 VirtioNetShutdownTx (Dev);
578
579 AbortDevice:
580 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
581
582 ReleaseTxRing:
583 VirtioNetUninitRing (Dev, &Dev->TxRing, Dev->TxRingMap);
584
585 ReleaseRxRing:
586 VirtioNetUninitRing (Dev, &Dev->RxRing, Dev->RxRingMap);
587
588 DeviceFailed:
589 //
590 // restore device status invariant for the EfiSimpleNetworkStarted state
591 //
592 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
593
594 InitFailed:
595 gBS->RestoreTPL (OldTpl);
596 return Status;
597 }