1 /** @file
2 
3   Implementation of the SNP.GetStatus() function and its private helpers if
4   any.
5 
6   Copyright (C) 2013, Red Hat, Inc.
7   Copyright (c) 2006 - 2014, 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/UefiBootServicesTableLib.h>
21 
22 #include "VirtioNet.h"
23 
24 /**
25   Reads the current interrupt status and recycled transmit buffer status from
26   a network interface.
27 
28   @param  This            The protocol instance pointer.
29   @param  InterruptStatus A pointer to the bit mask of the currently active
30                           interrupts If this is NULL, the interrupt status will
31                           not be read from the device. If this is not NULL, the
32                           interrupt status will be read from the device. When
33                           the  interrupt status is read, it will also be
34                           cleared. Clearing the transmit  interrupt does not
35                           empty the recycled transmit buffer array.
36   @param  TxBuf           Recycled transmit buffer address. The network
37                           interface will not transmit if its internal recycled
38                           transmit buffer array is full. Reading the transmit
39                           buffer does not clear the transmit interrupt. If this
40                           is NULL, then the transmit buffer status will not be
41                           read. If there are no transmit buffers to recycle and
42                           TxBuf is not NULL, * TxBuf will be set to NULL.
43 
44   @retval EFI_SUCCESS           The status of the network interface was
45                                 retrieved.
46   @retval EFI_NOT_STARTED       The network interface has not been started.
47   @retval EFI_INVALID_PARAMETER One or more of the parameters has an
48                                 unsupported value.
49   @retval EFI_DEVICE_ERROR      The command could not be sent to the network
50                                 interface.
51   @retval EFI_UNSUPPORTED       This function is not supported by the network
52                                 interface.
53 
54 **/
55 
56 EFI_STATUS
57 EFIAPI
VirtioNetGetStatus(IN EFI_SIMPLE_NETWORK_PROTOCOL * This,OUT UINT32 * InterruptStatus OPTIONAL,OUT VOID ** TxBuf OPTIONAL)58 VirtioNetGetStatus (
59   IN EFI_SIMPLE_NETWORK_PROTOCOL *This,
60   OUT UINT32                     *InterruptStatus OPTIONAL,
61   OUT VOID                       **TxBuf OPTIONAL
62   )
63 {
64   VNET_DEV   *Dev;
65   EFI_TPL    OldTpl;
66   EFI_STATUS Status;
67   UINT16     RxCurUsed;
68   UINT16     TxCurUsed;
69 
70   if (This == NULL) {
71     return EFI_INVALID_PARAMETER;
72   }
73 
74   Dev = VIRTIO_NET_FROM_SNP (This);
75   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
76   switch (Dev->Snm.State) {
77   case EfiSimpleNetworkStopped:
78     Status = EFI_NOT_STARTED;
79     goto Exit;
80   case EfiSimpleNetworkStarted:
81     Status = EFI_DEVICE_ERROR;
82     goto Exit;
83   default:
84     break;
85   }
86 
87   //
88   // update link status
89   //
90   if (Dev->Snm.MediaPresentSupported) {
91     UINT16 LinkStatus;
92 
93     Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus);
94     if (EFI_ERROR (Status)) {
95       goto Exit;
96     }
97     Dev->Snm.MediaPresent =
98       (BOOLEAN) ((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
99   }
100 
101   //
102   // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
103   //
104   MemoryFence ();
105   RxCurUsed = *Dev->RxRing.Used.Idx;
106   TxCurUsed = *Dev->TxRing.Used.Idx;
107   MemoryFence ();
108 
109   if (InterruptStatus != NULL) {
110     //
111     // report the receive interrupt if there is data available for reception,
112     // report the transmit interrupt if we have transmitted at least one buffer
113     //
114     *InterruptStatus = 0;
115     if (Dev->RxLastUsed != RxCurUsed) {
116       *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
117     }
118     if (Dev->TxLastUsed != TxCurUsed) {
119       ASSERT (Dev->TxCurPending > 0);
120       *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
121     }
122   }
123 
124   if (TxBuf != NULL) {
125     if (Dev->TxLastUsed == TxCurUsed) {
126       *TxBuf = NULL;
127     }
128     else {
129       UINT16 UsedElemIdx;
130       UINT32 DescIdx;
131 
132       //
133       // fetch the first descriptor among those that the hypervisor reports
134       // completed
135       //
136       ASSERT (Dev->TxCurPending > 0);
137       ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);
138 
139       UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize;
140       DescIdx = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id;
141       ASSERT (DescIdx < (UINT32) (2 * Dev->TxMaxPending - 1));
142 
143       //
144       // report buffer address to caller that has been enqueued by caller
145       //
146       *TxBuf = (VOID *)(UINTN) Dev->TxRing.Desc[DescIdx + 1].Addr;
147 
148       //
149       // now this descriptor can be used again to enqueue a transmit buffer
150       //
151       Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16) DescIdx;
152     }
153   }
154 
155   Status = EFI_SUCCESS;
156 
157 Exit:
158   gBS->RestoreTPL (OldTpl);
159   return Status;
160 }
161