1 /** @file
2 
3   Implementation of the SNP.Receive() function and its private helpers if any.
4 
5   Copyright (C) 2013, Red Hat, Inc.
6   Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
7 
8   This program and the accompanying materials are licensed and made available
9   under the terms and conditions of the BSD License which accompanies this
10   distribution. The full text of the license may be found at
11   http://opensource.org/licenses/bsd-license.php
12 
13   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 
16 **/
17 
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 
22 #include "VirtioNet.h"
23 
24 /**
25   Receives a packet from a network interface.
26 
27   @param  This       The protocol instance pointer.
28   @param  HeaderSize The size, in bytes, of the media header received on the
29                      network interface. If this parameter is NULL, then the
30                      media header size will not be returned.
31   @param  BufferSize On entry, the size, in bytes, of Buffer. On exit, the
32                      size, in bytes, of the packet that was received on the
33                      network interface.
34   @param  Buffer     A pointer to the data buffer to receive both the media
35                      header and the data.
36   @param  SrcAddr    The source HW MAC address. If this parameter is NULL, the
37                      HW MAC source address will not be extracted from the media
38                      header.
39   @param  DestAddr   The destination HW MAC address. If this parameter is NULL,
40                      the HW MAC destination address will not be extracted from
41                      the media header.
42   @param  Protocol   The media header type. If this parameter is NULL, then the
43                      protocol will not be extracted from the media header. See
44                      RFC 1700 section "Ether Types" for examples.
45 
46   @retval  EFI_SUCCESS           The received data was stored in Buffer, and
47                                  BufferSize has been updated to the number of
48                                  bytes received.
49   @retval  EFI_NOT_STARTED       The network interface has not been started.
50   @retval  EFI_NOT_READY         The network interface is too busy to accept
51                                  this transmit request.
52   @retval  EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
53   @retval  EFI_INVALID_PARAMETER One or more of the parameters has an
54                                  unsupported value.
55   @retval  EFI_DEVICE_ERROR      The command could not be sent to the network
56                                  interface.
57   @retval  EFI_UNSUPPORTED       This function is not supported by the network
58                                  interface.
59 
60 **/
61 
62 EFI_STATUS
63 EFIAPI
VirtioNetReceive(IN EFI_SIMPLE_NETWORK_PROTOCOL * This,OUT UINTN * HeaderSize OPTIONAL,IN OUT UINTN * BufferSize,OUT VOID * Buffer,OUT EFI_MAC_ADDRESS * SrcAddr OPTIONAL,OUT EFI_MAC_ADDRESS * DestAddr OPTIONAL,OUT UINT16 * Protocol OPTIONAL)64 VirtioNetReceive (
65   IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
66   OUT UINTN                      *HeaderSize OPTIONAL,
67   IN OUT UINTN                   *BufferSize,
68   OUT VOID                       *Buffer,
69   OUT EFI_MAC_ADDRESS            *SrcAddr    OPTIONAL,
70   OUT EFI_MAC_ADDRESS            *DestAddr   OPTIONAL,
71   OUT UINT16                     *Protocol   OPTIONAL
72   )
73 {
74   VNET_DEV   *Dev;
75   EFI_TPL    OldTpl;
76   EFI_STATUS Status;
77   UINT16     RxCurUsed;
78   UINT16     UsedElemIdx;
79   UINT32     DescIdx;
80   UINT32     RxLen;
81   UINTN      OrigBufferSize;
82   UINT8      *RxPtr;
83   UINT16     AvailIdx;
84   EFI_STATUS NotifyStatus;
85 
86   if (This == NULL || BufferSize == NULL || Buffer == NULL) {
87     return EFI_INVALID_PARAMETER;
88   }
89 
90   Dev = VIRTIO_NET_FROM_SNP (This);
91   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
92   switch (Dev->Snm.State) {
93   case EfiSimpleNetworkStopped:
94     Status = EFI_NOT_STARTED;
95     goto Exit;
96   case EfiSimpleNetworkStarted:
97     Status = EFI_DEVICE_ERROR;
98     goto Exit;
99   default:
100     break;
101   }
102 
103   //
104   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
105   //
106   MemoryFence ();
107   RxCurUsed = *Dev->RxRing.Used.Idx;
108   MemoryFence ();
109 
110   if (Dev->RxLastUsed == RxCurUsed) {
111     Status = EFI_NOT_READY;
112     goto Exit;
113   }
114 
115   UsedElemIdx = Dev->RxLastUsed % Dev->RxRing.QueueSize;
116   DescIdx = Dev->RxRing.Used.UsedElem[UsedElemIdx].Id;
117   RxLen   = Dev->RxRing.Used.UsedElem[UsedElemIdx].Len;
118 
119   //
120   // the virtio-net request header must be complete; we skip it
121   //
122   ASSERT (RxLen >= Dev->RxRing.Desc[DescIdx].Len);
123   RxLen -= Dev->RxRing.Desc[DescIdx].Len;
124   //
125   // the host must not have filled in more data than requested
126   //
127   ASSERT (RxLen <= Dev->RxRing.Desc[DescIdx + 1].Len);
128 
129   OrigBufferSize = *BufferSize;
130   *BufferSize = RxLen;
131 
132   if (OrigBufferSize < RxLen) {
133     Status = EFI_BUFFER_TOO_SMALL;
134     goto Exit; // keep the packet
135   }
136 
137   if (RxLen < Dev->Snm.MediaHeaderSize) {
138     Status = EFI_DEVICE_ERROR;
139     goto RecycleDesc; // drop useless short packet
140   }
141 
142   if (HeaderSize != NULL) {
143     *HeaderSize = Dev->Snm.MediaHeaderSize;
144   }
145 
146   RxPtr = (UINT8 *)(UINTN) Dev->RxRing.Desc[DescIdx + 1].Addr;
147   CopyMem (Buffer, RxPtr, RxLen);
148 
149   if (DestAddr != NULL) {
150     CopyMem (DestAddr, RxPtr, SIZE_OF_VNET (Mac));
151   }
152   RxPtr += SIZE_OF_VNET (Mac);
153 
154   if (SrcAddr != NULL) {
155     CopyMem (SrcAddr, RxPtr, SIZE_OF_VNET (Mac));
156   }
157   RxPtr += SIZE_OF_VNET (Mac);
158 
159   if (Protocol != NULL) {
160     *Protocol = (UINT16) ((RxPtr[0] << 8) | RxPtr[1]);
161   }
162   RxPtr += sizeof (UINT16);
163 
164   Status = EFI_SUCCESS;
165 
166 RecycleDesc:
167   ++Dev->RxLastUsed;
168 
169   //
170   // virtio-0.9.5, 2.4.1 Supplying Buffers to The Device
171   //
172   AvailIdx = *Dev->RxRing.Avail.Idx;
173   Dev->RxRing.Avail.Ring[AvailIdx++ % Dev->RxRing.QueueSize] =
174     (UINT16) DescIdx;
175 
176   MemoryFence ();
177   *Dev->RxRing.Avail.Idx = AvailIdx;
178 
179   MemoryFence ();
180   NotifyStatus = Dev->VirtIo->SetQueueNotify (Dev->VirtIo, VIRTIO_NET_Q_RX);
181   if (!EFI_ERROR (Status)) { // earlier error takes precedence
182     Status = NotifyStatus;
183   }
184 
185 Exit:
186   gBS->RestoreTPL (OldTpl);
187   return Status;
188 }
189