summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioNetDxe/SnpSharedHelpers.c
blob: d03255ab508f51484e5931499cbacaa8db520297 (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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/** @file

  Helper functions used by at least two Simple Network Protocol methods.

  Copyright (C) 2013, Red Hat, Inc.

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

**/

#include <Library/MemoryAllocationLib.h>

#include "VirtioNet.h"

//
// The user structure for the ordered collection that will track the mapping
// info of the packets queued in TxRing
//
typedef struct {
  VOID                    *Buffer;
  EFI_PHYSICAL_ADDRESS    DeviceAddress; // lookup key for reverse mapping
  VOID                    *BufMap;
} TX_BUF_MAP_INFO;

/**
  Release RX and TX resources on the boundary of the
  EfiSimpleNetworkInitialized state.

  These functions contribute to rolling back a partial, failed initialization
  of the virtio-net SNP driver instance, or to shutting down a fully
  initialized, running instance.

  They are only callable by the VirtioNetInitialize() and the
  VirtioNetShutdown() SNP methods. See the state diagram in "VirtioNet.h".

  @param[in,out] Dev  The VNET_DEV driver instance being shut down, or whose
                      partial, failed initialization is being rolled back.
*/
VOID
EFIAPI
VirtioNetShutdownRx (
  IN OUT VNET_DEV  *Dev
  )
{
  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RxBufMap);
  Dev->VirtIo->FreeSharedPages (
                 Dev->VirtIo,
                 Dev->RxBufNrPages,
                 Dev->RxBuf
                 );
}

VOID
EFIAPI
VirtioNetShutdownTx (
  IN OUT VNET_DEV  *Dev
  )
{
  ORDERED_COLLECTION_ENTRY  *Entry, *Entry2;
  TX_BUF_MAP_INFO           *TxBufMapInfo;
  VOID                      *UserStruct;

  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->TxSharedReqMap);
  Dev->VirtIo->FreeSharedPages (
                 Dev->VirtIo,
                 EFI_SIZE_TO_PAGES (sizeof *(Dev->TxSharedReq)),
                 Dev->TxSharedReq
                 );

  for (Entry = OrderedCollectionMin (Dev->TxBufCollection);
       Entry != NULL;
       Entry = Entry2)
  {
    Entry2 = OrderedCollectionNext (Entry);
    OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);
    TxBufMapInfo = UserStruct;
    Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);
    FreePool (TxBufMapInfo);
  }

  OrderedCollectionUninit (Dev->TxBufCollection);

  FreePool (Dev->TxFreeStack);
}

/**
  Release TX and RX VRING resources.

  @param[in,out] Dev       The VNET_DEV driver instance which was using
                           the ring.
  @param[in,out] Ring      The virtio ring to clean up.
  @param[in]     RingMap   A token return from the VirtioRingMap()
*/
VOID
EFIAPI
VirtioNetUninitRing (
  IN OUT VNET_DEV  *Dev,
  IN OUT VRING     *Ring,
  IN     VOID      *RingMap
  )
{
  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RingMap);
  VirtioRingUninit (Dev->VirtIo, Ring);
}

/**
  Map Caller-supplied TxBuf buffer to the device-mapped address

  @param[in]    Dev               The VNET_DEV driver instance which wants to
                                  map the Tx packet.
  @param[in]    Buffer            The system physical address of TxBuf
  @param[in]    NumberOfBytes     Number of bytes to map
  @param[out]   DeviceAddress     The resulting device address for the bus
                                  master access.

  @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to
                                  a lack of resources.
  @return                         Status codes from
                                  VirtioMapAllBytesInSharedBuffer()
  @retval EFI_SUCCESS             Caller-supplied buffer is successfully mapped.
*/
EFI_STATUS
EFIAPI
VirtioNetMapTxBuf (
  IN  VNET_DEV              *Dev,
  IN  VOID                  *Buffer,
  IN  UINTN                 NumberOfBytes,
  OUT EFI_PHYSICAL_ADDRESS  *DeviceAddress
  )
{
  EFI_STATUS            Status;
  TX_BUF_MAP_INFO       *TxBufMapInfo;
  EFI_PHYSICAL_ADDRESS  Address;
  VOID                  *Mapping;

  TxBufMapInfo = AllocatePool (sizeof (*TxBufMapInfo));
  if (TxBufMapInfo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Status = VirtioMapAllBytesInSharedBuffer (
             Dev->VirtIo,
             VirtioOperationBusMasterRead,
             Buffer,
             NumberOfBytes,
             &Address,
             &Mapping
             );
  if (EFI_ERROR (Status)) {
    goto FreeTxBufMapInfo;
  }

  TxBufMapInfo->Buffer        = Buffer;
  TxBufMapInfo->DeviceAddress = Address;
  TxBufMapInfo->BufMap        = Mapping;

  Status = OrderedCollectionInsert (
             Dev->TxBufCollection,
             NULL,
             TxBufMapInfo
             );
  switch (Status) {
    case EFI_OUT_OF_RESOURCES:
      goto UnmapTxBuf;
    case EFI_ALREADY_STARTED:
      //
      // This should never happen: it implies
      //
      // - an identity-mapping VIRTIO_DEVICE_PROTOCOL.MapSharedBuffer()
      //   implementation -- which is fine,
      //
      // - and an SNP client that queues multiple instances of the exact same
      //   buffer address with SNP.Transmit() -- which is undefined behavior,
      //   based on the TxBuf language in UEFI-2.7,
      //   EFI_SIMPLE_NETWORK.GetStatus().
      //
      ASSERT (FALSE);
      Status = EFI_INVALID_PARAMETER;
      goto UnmapTxBuf;
    default:
      ASSERT_EFI_ERROR (Status);
      break;
  }

  *DeviceAddress = Address;
  return EFI_SUCCESS;

UnmapTxBuf:
  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Mapping);

FreeTxBufMapInfo:
  FreePool (TxBufMapInfo);
  return Status;
}

/**
  Unmap (aka reverse mapping) device mapped TxBuf buffer to the system
  physical address

  @param[in]    Dev               The VNET_DEV driver instance which wants to
                                  reverse- and unmap the Tx packet.
  @param[out]   Buffer            The system physical address of TxBuf
  @param[in]    DeviceAddress     The device address for the TxBuf

  @retval EFI_INVALID_PARAMETER   The DeviceAddress is not mapped
  @retval EFI_SUCCESS             The TxBuf at DeviceAddress has been unmapped,
                                  and Buffer has been set to TxBuf's system
                                  physical address.

*/
EFI_STATUS
EFIAPI
VirtioNetUnmapTxBuf (
  IN  VNET_DEV              *Dev,
  OUT VOID                  **Buffer,
  IN  EFI_PHYSICAL_ADDRESS  DeviceAddress
  )
{
  ORDERED_COLLECTION_ENTRY  *Entry;
  TX_BUF_MAP_INFO           *TxBufMapInfo;
  VOID                      *UserStruct;

  Entry = OrderedCollectionFind (Dev->TxBufCollection, &DeviceAddress);
  if (Entry == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  OrderedCollectionDelete (Dev->TxBufCollection, Entry, &UserStruct);

  TxBufMapInfo = UserStruct;

  *Buffer = TxBufMapInfo->Buffer;
  Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, TxBufMapInfo->BufMap);
  FreePool (TxBufMapInfo);

  return EFI_SUCCESS;
}

/**
  Comparator function for two TX_BUF_MAP_INFO objects.

  @param[in] UserStruct1  Pointer to the first TX_BUF_MAP_INFO object.

  @param[in] UserStruct2  Pointer to the second TX_BUF_MAP_INFO object.

  @retval <0  If UserStruct1 compares less than UserStruct2.

  @retval  0  If UserStruct1 compares equal to UserStruct2.

  @retval >0  If UserStruct1 compares greater than UserStruct2.
*/
INTN
EFIAPI
VirtioNetTxBufMapInfoCompare (
  IN CONST VOID  *UserStruct1,
  IN CONST VOID  *UserStruct2
  )
{
  CONST TX_BUF_MAP_INFO  *MapInfo1;
  CONST TX_BUF_MAP_INFO  *MapInfo2;

  MapInfo1 = UserStruct1;
  MapInfo2 = UserStruct2;

  return MapInfo1->DeviceAddress < MapInfo2->DeviceAddress ? -1 :
         MapInfo1->DeviceAddress > MapInfo2->DeviceAddress ?  1 :
         0;
}

/**
  Compare a standalone DeviceAddress against a TX_BUF_MAP_INFO object
  containing an embedded DeviceAddress.

  @param[in] StandaloneKey  Pointer to DeviceAddress, which has type
                            EFI_PHYSICAL_ADDRESS.

  @param[in] UserStruct     Pointer to the TX_BUF_MAP_INFO object with the
                            embedded DeviceAddress.

  @retval <0  If StandaloneKey compares less than UserStruct's key.

  @retval  0  If StandaloneKey compares equal to UserStruct's key.

  @retval >0  If StandaloneKey compares greater than UserStruct's key.
**/
INTN
EFIAPI
VirtioNetTxBufDeviceAddressCompare (
  IN CONST VOID  *StandaloneKey,
  IN CONST VOID  *UserStruct
  )
{
  CONST EFI_PHYSICAL_ADDRESS  *DeviceAddress;
  CONST TX_BUF_MAP_INFO       *MapInfo;

  DeviceAddress = StandaloneKey;
  MapInfo       = UserStruct;

  return *DeviceAddress < MapInfo->DeviceAddress ? -1 :
         *DeviceAddress > MapInfo->DeviceAddress ?  1 :
         0;
}