• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
9   This program and the accompanying materials are licensed and made available
10   under the terms and conditions of the BSD License which accompanies this
11   distribution. The full text of the license may be found at
12   http://opensource.org/licenses/bsd-license.php
13 
14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
15   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 
17 **/
18 
19 #include <Library/BaseLib.h>
20 #include <Library/MemoryAllocationLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 
23 #include "VirtioNet.h"
24 
25 /**
26   Initialize a virtio ring for a specific transfer direction of the virtio-net
27   device.
28 
29   This function may only be called by VirtioNetInitialize().
30 
31   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
32                            EfiSimpleNetworkInitialized state.
33   @param[in]     Selector  Identifies the transfer direction (virtio queue) of
34                            the network device.
35   @param[out]    Ring      The virtio-ring inside the VNET_DEV structure,
36                            corresponding to Selector.
37 
38   @retval EFI_UNSUPPORTED  The queue size reported by the virtio-net device is
39                            too small.
40   @return                  Status codes from VIRTIO_CFG_WRITE(),
41                            VIRTIO_CFG_READ() and VirtioRingInit().
42   @retval EFI_SUCCESS      Ring initialized.
43 */
44 
45 STATIC
46 EFI_STATUS
47 EFIAPI
VirtioNetInitRing(IN OUT VNET_DEV * Dev,IN UINT16 Selector,OUT VRING * Ring)48 VirtioNetInitRing (
49   IN OUT VNET_DEV *Dev,
50   IN     UINT16   Selector,
51   OUT    VRING    *Ring
52   )
53 {
54   EFI_STATUS Status;
55   UINT16     QueueSize;
56 
57   //
58   // step 4b -- allocate selected queue
59   //
60   Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, Selector);
61   if (EFI_ERROR (Status)) {
62     return Status;
63   }
64   Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
65   if (EFI_ERROR (Status)) {
66     return Status;
67   }
68 
69   //
70   // For each packet (RX and TX alike), we need two descriptors:
71   // one for the virtio-net request header, and another one for the data
72   //
73   if (QueueSize < 2) {
74     return EFI_UNSUPPORTED;
75   }
76   Status = VirtioRingInit (QueueSize, Ring);
77   if (EFI_ERROR (Status)) {
78     return Status;
79   }
80 
81   //
82   // Additional steps for MMIO: align the queue appropriately, and set the
83   // size. If anything fails from here on, we must release the ring resources.
84   //
85   Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
86   if (EFI_ERROR (Status)) {
87     goto ReleaseQueue;
88   }
89 
90   Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
91   if (EFI_ERROR (Status)) {
92     goto ReleaseQueue;
93   }
94 
95   //
96   // step 4c -- report GPFN (guest-physical frame number) of queue
97   //
98   Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, Ring);
99   if (EFI_ERROR (Status)) {
100     goto ReleaseQueue;
101   }
102 
103   return EFI_SUCCESS;
104 
105 ReleaseQueue:
106   VirtioRingUninit (Ring);
107 
108   return Status;
109 }
110 
111 
112 /**
113   Set up static scaffolding for the VirtioNetTransmit() and
114   VirtioNetGetStatus() SNP methods.
115 
116   This function may only be called by VirtioNetInitialize().
117 
118   The structures laid out and resources configured include:
119   - fully populate the TX queue with a static pattern of virtio descriptor
120     chains,
121   - tracking of heads of free descriptor chains from the above,
122   - one common virtio-net request header (never modified by the host) for all
123     pending TX packets,
124   - select polling over TX interrupt.
125 
126   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
127                            EfiSimpleNetworkInitialized state.
128 
129   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the stack to track the heads
130                                 of free descriptor chains.
131   @retval EFI_SUCCESS           TX setup successful.
132 */
133 
134 STATIC
135 EFI_STATUS
136 EFIAPI
VirtioNetInitTx(IN OUT VNET_DEV * Dev)137 VirtioNetInitTx (
138   IN OUT VNET_DEV *Dev
139   )
140 {
141   UINTN TxSharedReqSize;
142   UINTN PktIdx;
143 
144   Dev->TxMaxPending = (UINT16) MIN (Dev->TxRing.QueueSize / 2,
145                                  VNET_MAX_PENDING);
146   Dev->TxCurPending = 0;
147   Dev->TxFreeStack  = AllocatePool (Dev->TxMaxPending *
148                         sizeof *Dev->TxFreeStack);
149   if (Dev->TxFreeStack == NULL) {
150     return EFI_OUT_OF_RESOURCES;
151   }
152 
153   //
154   // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
155   // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
156   //
157   TxSharedReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
158                     sizeof Dev->TxSharedReq.V0_9_5 :
159                     sizeof Dev->TxSharedReq;
160 
161   for (PktIdx = 0; PktIdx < Dev->TxMaxPending; ++PktIdx) {
162     UINT16 DescIdx;
163 
164     DescIdx = (UINT16) (2 * PktIdx);
165     Dev->TxFreeStack[PktIdx] = DescIdx;
166 
167     //
168     // For each possibly pending packet, lay out the descriptor for the common
169     // (unmodified by the host) virtio-net request header.
170     //
171     Dev->TxRing.Desc[DescIdx].Addr  = (UINTN) &Dev->TxSharedReq;
172     Dev->TxRing.Desc[DescIdx].Len   = (UINT32) TxSharedReqSize;
173     Dev->TxRing.Desc[DescIdx].Flags = VRING_DESC_F_NEXT;
174     Dev->TxRing.Desc[DescIdx].Next  = (UINT16) (DescIdx + 1);
175 
176     //
177     // The second descriptor of each pending TX packet is updated on the fly,
178     // but it always terminates the descriptor chain of the packet.
179     //
180     Dev->TxRing.Desc[DescIdx + 1].Flags = 0;
181   }
182 
183   //
184   // virtio-0.9.5, Appendix C, Packet Transmission
185   //
186   Dev->TxSharedReq.V0_9_5.Flags   = 0;
187   Dev->TxSharedReq.V0_9_5.GsoType = VIRTIO_NET_HDR_GSO_NONE;
188 
189   //
190   // For VirtIo 1.0 only -- the field exists, but it is unused
191   //
192   Dev->TxSharedReq.NumBuffers = 0;
193 
194   //
195   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
196   //
197   MemoryFence ();
198   Dev->TxLastUsed = *Dev->TxRing.Used.Idx;
199   ASSERT (Dev->TxLastUsed == 0);
200 
201   //
202   // want no interrupt when a transmit completes
203   //
204   *Dev->TxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
205 
206   return EFI_SUCCESS;
207 }
208 
209 
210 /**
211   Set up static scaffolding for the VirtioNetReceive() SNP method and enable
212   live device operation.
213 
214   This function may only be called as VirtioNetInitialize()'s final step.
215 
216   The structures laid out and resources configured include:
217   - destination area for the host to write virtio-net request headers and
218     packet data into,
219   - select polling over RX interrupt,
220   - fully populate the RX queue with a static pattern of virtio descriptor
221     chains.
222 
223   @param[in,out] Dev       The VNET_DEV driver instance about to enter the
224                            EfiSimpleNetworkInitialized state.
225 
226   @retval EFI_OUT_OF_RESOURCES  Failed to allocate RX destination area.
227   @return                       Status codes from VIRTIO_CFG_WRITE().
228   @retval EFI_SUCCESS           RX setup successful. The device is live and may
229                                 already be writing to the receive area.
230 */
231 
232 STATIC
233 EFI_STATUS
234 EFIAPI
VirtioNetInitRx(IN OUT VNET_DEV * Dev)235 VirtioNetInitRx (
236   IN OUT VNET_DEV *Dev
237   )
238 {
239   EFI_STATUS Status;
240   UINTN      VirtioNetReqSize;
241   UINTN      RxBufSize;
242   UINT16     RxAlwaysPending;
243   UINTN      PktIdx;
244   UINT16     DescIdx;
245   UINT8      *RxPtr;
246 
247   //
248   // In VirtIo 1.0, the NumBuffers field is mandatory. In 0.9.5, it depends on
249   // VIRTIO_NET_F_MRG_RXBUF, which we never negotiate.
250   //
251   VirtioNetReqSize = (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) ?
252                      sizeof (VIRTIO_NET_REQ) :
253                      sizeof (VIRTIO_1_0_NET_REQ);
254 
255   //
256   // For each incoming packet we must supply two descriptors:
257   // - the recipient for the virtio-net request header, plus
258   // - the recipient for the network data (which consists of Ethernet header
259   //   and Ethernet payload).
260   //
261   RxBufSize = VirtioNetReqSize +
262               (Dev->Snm.MediaHeaderSize + Dev->Snm.MaxPacketSize);
263 
264   //
265   // Limit the number of pending RX packets if the queue is big. The division
266   // by two is due to the above "two descriptors per packet" trait.
267   //
268   RxAlwaysPending = (UINT16) MIN (Dev->RxRing.QueueSize / 2, VNET_MAX_PENDING);
269 
270   Dev->RxBuf = AllocatePool (RxAlwaysPending * RxBufSize);
271   if (Dev->RxBuf == NULL) {
272     return EFI_OUT_OF_RESOURCES;
273   }
274 
275   //
276   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
277   //
278   MemoryFence ();
279   Dev->RxLastUsed = *Dev->RxRing.Used.Idx;
280   ASSERT (Dev->RxLastUsed == 0);
281 
282   //
283   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device:
284   // the host should not send interrupts, we'll poll in VirtioNetReceive()
285   // and VirtioNetIsPacketAvailable().
286   //
287   *Dev->RxRing.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
288 
289   //
290   // now set up a separate, two-part descriptor chain for each RX packet, and
291   // link each chain into (from) the available ring as well
292   //
293   DescIdx = 0;
294   RxPtr = Dev->RxBuf;
295   for (PktIdx = 0; PktIdx < RxAlwaysPending; ++PktIdx) {
296     //
297     // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
298     // invisible to the host until we update the Index Field
299     //
300     Dev->RxRing.Avail.Ring[PktIdx] = DescIdx;
301 
302     //
303     // virtio-0.9.5, 2.4.1.1 Placing Buffers into the Descriptor Table
304     //
305     Dev->RxRing.Desc[DescIdx].Addr  = (UINTN) RxPtr;
306     Dev->RxRing.Desc[DescIdx].Len   = (UINT32) VirtioNetReqSize;
307     Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
308     Dev->RxRing.Desc[DescIdx].Next  = (UINT16) (DescIdx + 1);
309     RxPtr += Dev->RxRing.Desc[DescIdx++].Len;
310 
311     Dev->RxRing.Desc[DescIdx].Addr  = (UINTN) RxPtr;
312     Dev->RxRing.Desc[DescIdx].Len   = (UINT32) (RxBufSize - VirtioNetReqSize);
313     Dev->RxRing.Desc[DescIdx].Flags = VRING_DESC_F_WRITE;
314     RxPtr += Dev->RxRing.Desc[DescIdx++].Len;
315   }
316 
317   //
318   // virtio-0.9.5, 2.4.1.3 Updating the Index Field
319   //
320   MemoryFence ();
321   *Dev->RxRing.Avail.Idx = RxAlwaysPending;
322 
323   //
324   // At this point reception may already be running. In order to make it sure,
325   // kick the hypervisor. If we fail to kick it, we must first abort reception
326   // before tearing down anything, because reception may have been already
327   // running even without the kick.
328   //
329   // virtio-0.9.5, 2.4.1.4 Notifying the Device
330   //
331   MemoryFence ();
332   Status = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
333   if (EFI_ERROR (Status)) {
334     Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
335     FreePool (Dev->RxBuf);
336   }
337 
338   return Status;
339 }
340 
341 
342 /**
343   Resets a network adapter and allocates the transmit and receive buffers
344   required by the network interface; optionally, also requests allocation  of
345   additional transmit and receive buffers.
346 
347   @param  This              The protocol instance pointer.
348   @param  ExtraRxBufferSize The size, in bytes, of the extra receive buffer
349                             space that the driver should allocate for the
350                             network interface. Some network interfaces will not
351                             be able to use the extra buffer, and the caller
352                             will not know if it is actually being used.
353   @param  ExtraTxBufferSize The size, in bytes, of the extra transmit buffer
354                             space that the driver should allocate for the
355                             network interface. Some network interfaces will not
356                             be able to use the extra buffer, and the caller
357                             will not know if it is actually being used.
358 
359   @retval EFI_SUCCESS           The network interface was initialized.
360   @retval EFI_NOT_STARTED       The network interface has not been started.
361   @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit
362                                 and receive buffers.
363   @retval EFI_INVALID_PARAMETER One or more of the parameters has an
364                                 unsupported value.
365   @retval EFI_DEVICE_ERROR      The command could not be sent to the network
366                                 interface.
367   @retval EFI_UNSUPPORTED       This function is not supported by the network
368                                 interface.
369 
370 **/
371 
372 EFI_STATUS
373 EFIAPI
VirtioNetInitialize(IN EFI_SIMPLE_NETWORK_PROTOCOL * This,IN UINTN ExtraRxBufferSize OPTIONAL,IN UINTN ExtraTxBufferSize OPTIONAL)374 VirtioNetInitialize (
375   IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
376   IN UINTN                       ExtraRxBufferSize  OPTIONAL,
377   IN UINTN                       ExtraTxBufferSize  OPTIONAL
378   )
379 {
380   VNET_DEV   *Dev;
381   EFI_TPL    OldTpl;
382   EFI_STATUS Status;
383   UINT8      NextDevStat;
384   UINT64     Features;
385 
386   if (This == NULL) {
387     return EFI_INVALID_PARAMETER;
388   }
389   if (ExtraRxBufferSize > 0 || ExtraTxBufferSize > 0) {
390     return EFI_UNSUPPORTED;
391   }
392 
393   Dev = VIRTIO_NET_FROM_SNP (This);
394   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
395   if (Dev->Snm.State != EfiSimpleNetworkStarted) {
396     Status = EFI_NOT_STARTED;
397     goto InitFailed;
398   }
399 
400   //
401   // In the EfiSimpleNetworkStarted state the virtio-net device has status
402   // value 0 (= reset) -- see the state diagram, the full call chain to
403   // the end of VirtioNetGetFeatures() (considering we're here now),
404   // the DeviceFailed label below, and VirtioNetShutdown().
405   //
406   // Accordingly, the below is a subsequence of the steps found in the
407   // virtio-0.9.5 spec, 2.2.1 Device Initialization Sequence.
408   //
409   NextDevStat = VSTAT_ACK;    // step 2 -- acknowledge device presence
410   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
411   if (EFI_ERROR (Status)) {
412     goto InitFailed;
413   }
414 
415   NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
416   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
417   if (EFI_ERROR (Status)) {
418     goto DeviceFailed;
419   }
420 
421   //
422   // Set Page Size - MMIO VirtIo Specific
423   //
424   Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
425   if (EFI_ERROR (Status)) {
426     goto DeviceFailed;
427   }
428 
429   //
430   // step 4a -- retrieve features. Note that we're past validating required
431   // features in VirtioNetGetFeatures().
432   //
433   Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
434   if (EFI_ERROR (Status)) {
435     goto DeviceFailed;
436   }
437 
438   ASSERT (Features & VIRTIO_NET_F_MAC);
439   ASSERT (Dev->Snm.MediaPresentSupported ==
440     !!(Features & VIRTIO_NET_F_STATUS));
441 
442   Features &= VIRTIO_NET_F_MAC | VIRTIO_NET_F_STATUS | VIRTIO_F_VERSION_1;
443 
444   //
445   // In virtio-1.0, feature negotiation is expected to complete before queue
446   // discovery, and the device can also reject the selected set of features.
447   //
448   if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
449     Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
450     if (EFI_ERROR (Status)) {
451       goto DeviceFailed;
452     }
453   }
454 
455   //
456   // step 4b, 4c -- allocate and report virtqueues
457   //
458   Status = VirtioNetInitRing (Dev, VIRTIO_NET_Q_RX, &Dev->RxRing);
459   if (EFI_ERROR (Status)) {
460     goto DeviceFailed;
461   }
462 
463   Status = VirtioNetInitRing (Dev, VIRTIO_NET_Q_TX, &Dev->TxRing);
464   if (EFI_ERROR (Status)) {
465     goto ReleaseRxRing;
466   }
467 
468   //
469   // step 5 -- keep only the features we want
470   //
471   if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
472     Features &= ~(UINT64)VIRTIO_F_VERSION_1;
473     Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
474     if (EFI_ERROR (Status)) {
475       goto ReleaseTxRing;
476     }
477   }
478 
479   //
480   // step 6 -- virtio-net initialization complete
481   //
482   NextDevStat |= VSTAT_DRIVER_OK;
483   Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
484   if (EFI_ERROR (Status)) {
485     goto ReleaseTxRing;
486   }
487 
488   Status = VirtioNetInitTx (Dev);
489   if (EFI_ERROR (Status)) {
490     goto AbortDevice;
491   }
492 
493   //
494   // start receiving
495   //
496   Status = VirtioNetInitRx (Dev);
497   if (EFI_ERROR (Status)) {
498     goto ReleaseTxAux;
499   }
500 
501   Dev->Snm.State = EfiSimpleNetworkInitialized;
502   gBS->RestoreTPL (OldTpl);
503   return EFI_SUCCESS;
504 
505 ReleaseTxAux:
506   VirtioNetShutdownTx (Dev);
507 
508 AbortDevice:
509   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
510 
511 ReleaseTxRing:
512   VirtioRingUninit (&Dev->TxRing);
513 
514 ReleaseRxRing:
515   VirtioRingUninit (&Dev->RxRing);
516 
517 DeviceFailed:
518   //
519   // restore device status invariant for the EfiSimpleNetworkStarted state
520   //
521   Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
522 
523 InitFailed:
524   gBS->RestoreTPL (OldTpl);
525   return Status;
526 }
527