summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioNetDxe/SnpGetStatus.c
blob: 0e13bdc78ce2e0d8286d353186013063636989cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/** @file

  Implementation of the SNP.GetStatus() function and its private helpers if
  any.

  Copyright (C) 2013, Red Hat, Inc.
  Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/BaseLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include "VirtioNet.h"

/**
  Reads the current interrupt status and recycled transmit buffer status from
  a network interface.

  @param  This            The protocol instance pointer.
  @param  InterruptStatus A pointer to the bit mask of the currently active
                          interrupts If this is NULL, the interrupt status will
                          not be read from the device. If this is not NULL, the
                          interrupt status will be read from the device. When
                          the  interrupt status is read, it will also be
                          cleared. Clearing the transmit  interrupt does not
                          empty the recycled transmit buffer array.
  @param  TxBuf           Recycled transmit buffer address. The network
                          interface will not transmit if its internal recycled
                          transmit buffer array is full. Reading the transmit
                          buffer does not clear the transmit interrupt. If this
                          is NULL, then the transmit buffer status will not be
                          read. If there are no transmit buffers to recycle and
                          TxBuf is not NULL, * TxBuf will be set to NULL.

  @retval EFI_SUCCESS           The status of the network interface was
                                retrieved.
  @retval EFI_NOT_STARTED       The network interface has not been started.
  @retval EFI_INVALID_PARAMETER One or more of the parameters has an
                                unsupported value.
  @retval EFI_DEVICE_ERROR      The command could not be sent to the network
                                interface.
  @retval EFI_UNSUPPORTED       This function is not supported by the network
                                interface.

**/
EFI_STATUS
EFIAPI
VirtioNetGetStatus (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This,
  OUT UINT32                      *InterruptStatus OPTIONAL,
  OUT VOID                        **TxBuf OPTIONAL
  )
{
  VNET_DEV              *Dev;
  EFI_TPL               OldTpl;
  EFI_STATUS            Status;
  UINT16                RxCurUsed;
  UINT16                TxCurUsed;
  EFI_PHYSICAL_ADDRESS  DeviceAddress;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Dev    = VIRTIO_NET_FROM_SNP (This);
  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
  switch (Dev->Snm.State) {
    case EfiSimpleNetworkStopped:
      Status = EFI_NOT_STARTED;
      goto Exit;
    case EfiSimpleNetworkStarted:
      Status = EFI_DEVICE_ERROR;
      goto Exit;
    default:
      break;
  }

  //
  // update link status
  //
  if (Dev->Snm.MediaPresentSupported) {
    UINT16  LinkStatus;

    Status = VIRTIO_CFG_READ (Dev, LinkStatus, &LinkStatus);
    if (EFI_ERROR (Status)) {
      goto Exit;
    }

    Dev->Snm.MediaPresent =
      (BOOLEAN)((LinkStatus & VIRTIO_NET_S_LINK_UP) != 0);
  }

  //
  // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
  //
  MemoryFence ();
  RxCurUsed = *Dev->RxRing.Used.Idx;
  TxCurUsed = *Dev->TxRing.Used.Idx;
  MemoryFence ();

  if (InterruptStatus != NULL) {
    //
    // report the receive interrupt if there is data available for reception,
    // report the transmit interrupt if we have transmitted at least one buffer
    //
    *InterruptStatus = 0;
    if (Dev->RxLastUsed != RxCurUsed) {
      *InterruptStatus |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
    }

    if (Dev->TxLastUsed != TxCurUsed) {
      ASSERT (Dev->TxCurPending > 0);
      *InterruptStatus |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
    }
  }

  if (TxBuf != NULL) {
    if (Dev->TxLastUsed == TxCurUsed) {
      *TxBuf = NULL;
    } else {
      UINT16  UsedElemIdx;
      UINT32  DescIdx;

      //
      // fetch the first descriptor among those that the hypervisor reports
      // completed
      //
      ASSERT (Dev->TxCurPending > 0);
      ASSERT (Dev->TxCurPending <= Dev->TxMaxPending);

      UsedElemIdx = Dev->TxLastUsed++ % Dev->TxRing.QueueSize;
      DescIdx     = Dev->TxRing.Used.UsedElem[UsedElemIdx].Id;
      ASSERT (DescIdx < (UINT32)(2 * Dev->TxMaxPending - 1));

      //
      // get the device address that has been enqueued for the caller's
      // transmit buffer
      //
      DeviceAddress = Dev->TxRing.Desc[DescIdx + 1].Addr;

      //
      // now this descriptor can be used again to enqueue a transmit buffer
      //
      Dev->TxFreeStack[--Dev->TxCurPending] = (UINT16)DescIdx;

      //
      // Unmap the device address and perform the reverse mapping to find the
      // caller buffer address.
      //
      Status = VirtioNetUnmapTxBuf (
                 Dev,
                 TxBuf,
                 DeviceAddress
                 );
      if (EFI_ERROR (Status)) {
        //
        // VirtioNetUnmapTxBuf should never fail, if we have reached here
        // that means our internal state has been corrupted
        //
        ASSERT (FALSE);
        Status = EFI_DEVICE_ERROR;
        goto Exit;
      }
    }
  }

  Status = EFI_SUCCESS;

Exit:
  gBS->RestoreTPL (OldTpl);
  return Status;
}