Coverage Report

Created: 2026-04-04 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/edk2/OvmfPkg/Library/VirtioLib/VirtioLib.c
Line
Count
Source
1
/** @file
2
3
  Utility functions used by virtio device drivers.
4
5
  Copyright (C) 2012-2016, Red Hat, Inc.
6
  Portion of Copyright (C) 2013, ARM Ltd.
7
  Copyright (C) 2017, AMD Inc, All rights reserved.<BR>
8
9
  SPDX-License-Identifier: BSD-2-Clause-Patent
10
11
**/
12
13
#include <Library/BaseLib.h>
14
#include <Library/BaseMemoryLib.h>
15
#include <Library/DebugLib.h>
16
#include <Library/UefiBootServicesTableLib.h>
17
18
#include <Library/VirtioLib.h>
19
20
/**
21
22
  Configure a virtio ring.
23
24
  This function sets up internal storage (the guest-host communication area)
25
  and lays out several "navigation" (ie. no-ownership) pointers to parts of
26
  that storage.
27
28
  Relevant sections from the virtio-0.9.5 spec:
29
  - 1.1 Virtqueues,
30
  - 2.3 Virtqueue Configuration.
31
32
  @param[in]  VirtIo            The virtio device which will use the ring.
33
34
  @param[in]                    The number of descriptors to allocate for the
35
                                virtio ring, as requested by the host.
36
37
  @param[out] Ring              The virtio ring to set up.
38
39
  @return                       Status codes propagated from
40
                                VirtIo->AllocateSharedPages().
41
42
  @retval EFI_SUCCESS           Allocation and setup successful. Ring->Base
43
                                (and nothing else) is responsible for
44
                                deallocation.
45
46
**/
47
EFI_STATUS
48
EFIAPI
49
VirtioRingInit (
50
  IN  VIRTIO_DEVICE_PROTOCOL  *VirtIo,
51
  IN  UINT16                  QueueSize,
52
  OUT VRING                   *Ring
53
  )
54
144
{
55
144
  EFI_STATUS      Status;
56
144
  UINTN           RingSize;
57
144
  volatile UINT8  *RingPagesPtr;
58
59
144
  RingSize = ALIGN_VALUE (
60
144
               sizeof *Ring->Desc            * QueueSize +
61
144
               sizeof *Ring->Avail.Flags                 +
62
144
               sizeof *Ring->Avail.Idx                   +
63
144
               sizeof *Ring->Avail.Ring      * QueueSize +
64
144
               sizeof *Ring->Avail.UsedEvent,
65
144
               EFI_PAGE_SIZE
66
144
               );
67
68
144
  RingSize += ALIGN_VALUE (
69
144
                sizeof *Ring->Used.Flags                  +
70
144
                sizeof *Ring->Used.Idx                    +
71
144
                sizeof *Ring->Used.UsedElem   * QueueSize +
72
144
                sizeof *Ring->Used.AvailEvent,
73
144
                EFI_PAGE_SIZE
74
144
                );
75
76
  //
77
  // Allocate a shared ring buffer
78
  //
79
144
  Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);
80
144
  Status         = VirtIo->AllocateSharedPages (
81
144
                             VirtIo,
82
144
                             Ring->NumPages,
83
144
                             &Ring->Base
84
144
                             );
85
144
  if (EFI_ERROR (Status)) {
86
0
    return Status;
87
0
  }
88
89
144
  SetMem (Ring->Base, RingSize, 0x00);
90
144
  RingPagesPtr = Ring->Base;
91
92
144
  Ring->Desc    = (volatile VOID *)RingPagesPtr;
93
144
  RingPagesPtr += sizeof *Ring->Desc * QueueSize;
94
95
144
  Ring->Avail.Flags = (volatile VOID *)RingPagesPtr;
96
144
  RingPagesPtr     += sizeof *Ring->Avail.Flags;
97
98
144
  Ring->Avail.Idx = (volatile VOID *)RingPagesPtr;
99
144
  RingPagesPtr   += sizeof *Ring->Avail.Idx;
100
101
144
  Ring->Avail.Ring = (volatile VOID *)RingPagesPtr;
102
144
  RingPagesPtr    += sizeof *Ring->Avail.Ring * QueueSize;
103
104
144
  Ring->Avail.UsedEvent = (volatile VOID *)RingPagesPtr;
105
144
  RingPagesPtr         += sizeof *Ring->Avail.UsedEvent;
106
107
144
  RingPagesPtr = (volatile UINT8 *)Ring->Base +
108
144
                 ALIGN_VALUE (
109
144
                   RingPagesPtr - (volatile UINT8 *)Ring->Base,
110
144
                   EFI_PAGE_SIZE
111
144
                   );
112
113
144
  Ring->Used.Flags = (volatile VOID *)RingPagesPtr;
114
144
  RingPagesPtr    += sizeof *Ring->Used.Flags;
115
116
144
  Ring->Used.Idx = (volatile VOID *)RingPagesPtr;
117
144
  RingPagesPtr  += sizeof *Ring->Used.Idx;
118
119
144
  Ring->Used.UsedElem = (volatile VOID *)RingPagesPtr;
120
144
  RingPagesPtr       += sizeof *Ring->Used.UsedElem * QueueSize;
121
122
144
  Ring->Used.AvailEvent = (volatile VOID *)RingPagesPtr;
123
144
  RingPagesPtr         += sizeof *Ring->Used.AvailEvent;
124
125
144
  Ring->QueueSize = QueueSize;
126
144
  return EFI_SUCCESS;
127
144
}
128
129
/**
130
131
  Tear down the internal resources of a configured virtio ring.
132
133
  The caller is responsible to stop the host from using this ring before
134
  invoking this function: the VSTAT_DRIVER_OK bit must be clear in
135
  VhdrDeviceStatus.
136
137
  @param[in]  VirtIo  The virtio device which was using the ring.
138
139
  @param[out] Ring    The virtio ring to clean up.
140
141
**/
142
VOID
143
EFIAPI
144
VirtioRingUninit (
145
  IN     VIRTIO_DEVICE_PROTOCOL  *VirtIo,
146
  IN OUT VRING                   *Ring
147
  )
148
0
{
149
0
  VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base);
150
0
  SetMem (Ring, sizeof *Ring, 0x00);
151
0
}
152
153
/**
154
155
  Turn off interrupt notifications from the host, and prepare for appending
156
  multiple descriptors to the virtio ring.
157
158
  The calling driver must be in VSTAT_DRIVER_OK state.
159
160
  @param[in,out] Ring  The virtio ring we intend to append descriptors to.
161
162
  @param[out] Indices  The DESC_INDICES structure to initialize.
163
164
**/
165
VOID
166
EFIAPI
167
VirtioPrepare (
168
  IN OUT VRING         *Ring,
169
  OUT    DESC_INDICES  *Indices
170
  )
171
0
{
172
  //
173
  // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.
174
  // We're going to poll the answer, the host should not send an interrupt.
175
  //
176
0
  *Ring->Avail.Flags = (UINT16)VRING_AVAIL_F_NO_INTERRUPT;
177
178
  //
179
  // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
180
  //
181
  // Since we support only one in-flight descriptor chain, we can always build
182
  // that chain starting at entry #0 of the descriptor table.
183
  //
184
0
  Indices->HeadDescIdx = 0;
185
0
  Indices->NextDescIdx = Indices->HeadDescIdx;
186
0
}
187
188
/**
189
190
  Append a contiguous buffer for transmission / reception via the virtio ring.
191
192
  This function implements the following section from virtio-0.9.5:
193
  - 2.4.1.1 Placing Buffers into the Descriptor Table
194
195
  Free space is taken as granted, since the individual drivers support only
196
  synchronous requests and host side status is processed in lock-step with
197
  request submission. It is the calling driver's responsibility to verify the
198
  ring size in advance.
199
200
  The caller is responsible for initializing *Indices with VirtioPrepare()
201
  first.
202
203
  @param[in,out] Ring               The virtio ring to append the buffer to,
204
                                    as a descriptor.
205
206
  @param[in] BufferDeviceAddress    (Bus master device) start address of the
207
                                    transmit / receive buffer.
208
209
  @param[in] BufferSize             Number of bytes to transmit or receive.
210
211
  @param[in] Flags                  A bitmask of VRING_DESC_F_* flags. The
212
                                    caller computes this mask dependent on
213
                                    further buffers to append and transfer
214
                                    direction. VRING_DESC_F_INDIRECT is
215
                                    unsupported. The VRING_DESC.Next field is
216
                                    always set, but the host only interprets
217
                                    it dependent on VRING_DESC_F_NEXT.
218
219
  @param[in,out] Indices            Indices->HeadDescIdx is not accessed.
220
                                    On input, Indices->NextDescIdx identifies
221
                                    the next descriptor to carry the buffer.
222
                                    On output, Indices->NextDescIdx is
223
                                    incremented by one, modulo 2^16.
224
225
**/
226
VOID
227
EFIAPI
228
VirtioAppendDesc (
229
  IN OUT VRING         *Ring,
230
  IN     UINT64        BufferDeviceAddress,
231
  IN     UINT32        BufferSize,
232
  IN     UINT16        Flags,
233
  IN OUT DESC_INDICES  *Indices
234
  )
235
0
{
236
0
  volatile VRING_DESC  *Desc;
237
238
0
  Desc        = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];
239
0
  Desc->Addr  = BufferDeviceAddress;
240
0
  Desc->Len   = BufferSize;
241
0
  Desc->Flags = Flags;
242
0
  Desc->Next  = Indices->NextDescIdx % Ring->QueueSize;
243
0
}
244
245
/**
246
247
  Notify the host about the descriptor chain just built, and wait until the
248
  host processes it.
249
250
  @param[in] VirtIo       The target virtio device to notify.
251
252
  @param[in] VirtQueueId  Identifies the queue for the target device.
253
254
  @param[in,out] Ring     The virtio ring with descriptors to submit.
255
256
  @param[in] Indices      Indices->NextDescIdx is not accessed.
257
                          Indices->HeadDescIdx identifies the head descriptor
258
                          of the descriptor chain.
259
260
  @param[out] UsedLen     On success, the total number of bytes, consecutively
261
                          across the buffers linked by the descriptor chain,
262
                          that the host wrote. May be NULL if the caller
263
                          doesn't care, or can compute the same information
264
                          from device-specific request structures linked by the
265
                          descriptor chain.
266
267
  @return              Error code from VirtIo->SetQueueNotify() if it fails.
268
269
  @retval EFI_SUCCESS  Otherwise, the host processed all descriptors.
270
271
**/
272
EFI_STATUS
273
EFIAPI
274
VirtioFlush (
275
  IN     VIRTIO_DEVICE_PROTOCOL  *VirtIo,
276
  IN     UINT16                  VirtQueueId,
277
  IN OUT VRING                   *Ring,
278
  IN     DESC_INDICES            *Indices,
279
  OUT    UINT32                  *UsedLen    OPTIONAL
280
  )
281
0
{
282
0
  UINT16      NextAvailIdx;
283
0
  UINT16      LastUsedIdx;
284
0
  EFI_STATUS  Status;
285
0
  UINTN       PollPeriodUsecs;
286
287
  //
288
  // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
289
  //
290
  // It is not exactly clear from the wording of the virtio-0.9.5
291
  // specification, but each entry in the Available Ring references only the
292
  // head descriptor of any given descriptor chain.
293
  //
294
0
  NextAvailIdx = *Ring->Avail.Idx;
295
  //
296
  // (Due to our lock-step progress, this is where the host will produce the
297
  // used element with the head descriptor's index in it.)
298
  //
299
0
  LastUsedIdx                                        = NextAvailIdx;
300
0
  Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =
301
0
    Indices->HeadDescIdx % Ring->QueueSize;
302
303
  //
304
  // virtio-0.9.5, 2.4.1.3 Updating the Index Field
305
  //
306
0
  MemoryFence ();
307
0
  *Ring->Avail.Idx = NextAvailIdx;
308
309
  //
310
  // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
311
  // OK.
312
  //
313
0
  MemoryFence ();
314
0
  Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);
315
0
  if (EFI_ERROR (Status)) {
316
0
    return Status;
317
0
  }
318
319
  //
320
  // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
321
  // Wait until the host processes and acknowledges our descriptor chain. The
322
  // condition we use for polling is greatly simplified and relies on the
323
  // synchronous, lock-step progress.
324
  //
325
  // Keep slowing down until we reach a poll period of slightly above 1 ms.
326
  //
327
0
  PollPeriodUsecs = 1;
328
0
  MemoryFence ();
329
0
  while (*Ring->Used.Idx != NextAvailIdx) {
330
0
    gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay
331
332
0
    if (PollPeriodUsecs < 1024) {
333
0
      PollPeriodUsecs *= 2;
334
0
    }
335
336
0
    MemoryFence ();
337
0
  }
338
339
0
  MemoryFence ();
340
341
0
  if (UsedLen != NULL) {
342
0
    volatile CONST VRING_USED_ELEM  *UsedElem;
343
344
0
    UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize];
345
0
    ASSERT (UsedElem->Id == Indices->HeadDescIdx);
346
0
    *UsedLen = UsedElem->Len;
347
0
  }
348
349
0
  return EFI_SUCCESS;
350
0
}
351
352
/**
353
354
  Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver
355
  understands.
356
357
  In VirtIo 1.0, a device can reject a self-inconsistent feature bitmap through
358
  the new VSTAT_FEATURES_OK status bit. (For example if the driver requests a
359
  higher level feature but clears a prerequisite feature.) This function is a
360
  small wrapper around VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures() that also
361
  verifies if the VirtIo 1.0 device accepts the feature bitmap.
362
363
  @param[in]     VirtIo        Report feature bits to this device.
364
365
  @param[in]     Features      The set of feature bits that the driver wishes
366
                               to report. The caller is responsible to perform
367
                               any masking before calling this function; the
368
                               value is directly written with
369
                               VIRTIO_DEVICE_PROTOCOL.SetGuestFeatures().
370
371
  @param[in,out] DeviceStatus  On input, the status byte most recently written
372
                               to the device's status register. On output (even
373
                               on error), DeviceStatus will be updated so that
374
                               it is suitable for further status bit
375
                               manipulation and writing to the device's status
376
                               register.
377
378
  @retval  EFI_SUCCESS      The device accepted the configuration in Features.
379
380
  @return  EFI_UNSUPPORTED  The device rejected the configuration in Features.
381
382
  @retval  EFI_UNSUPPORTED  VirtIo->Revision is smaller than 1.0.0.
383
384
  @return                   Error codes from the SetGuestFeatures(),
385
                            SetDeviceStatus(), GetDeviceStatus() member
386
                            functions.
387
388
**/
389
EFI_STATUS
390
EFIAPI
391
Virtio10WriteFeatures (
392
  IN     VIRTIO_DEVICE_PROTOCOL  *VirtIo,
393
  IN     UINT64                  Features,
394
  IN OUT UINT8                   *DeviceStatus
395
  )
396
75
{
397
75
  EFI_STATUS  Status;
398
399
75
  if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
400
0
    return EFI_UNSUPPORTED;
401
0
  }
402
403
75
  Status = VirtIo->SetGuestFeatures (VirtIo, Features);
404
75
  if (EFI_ERROR (Status)) {
405
0
    return Status;
406
0
  }
407
408
75
  *DeviceStatus |= VSTAT_FEATURES_OK;
409
75
  Status         = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus);
410
75
  if (EFI_ERROR (Status)) {
411
0
    return Status;
412
0
  }
413
414
75
  Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus);
415
75
  if (EFI_ERROR (Status)) {
416
0
    return Status;
417
0
  }
418
419
75
  if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) {
420
0
    Status = EFI_UNSUPPORTED;
421
0
  }
422
423
75
  return Status;
424
75
}
425
426
/**
427
  Provides the virtio device address required to access system memory from a
428
  DMA bus master.
429
430
  The interface follows the same usage pattern as defined in UEFI spec 2.6
431
  (Section 13.2 PCI Root Bridge I/O Protocol)
432
433
  The VirtioMapAllBytesInSharedBuffer() is similar to VIRTIO_MAP_SHARED
434
  with exception that NumberOfBytes is IN-only parameter. The function
435
  maps all the bytes specified in NumberOfBytes param in one consecutive
436
  range.
437
438
  @param[in]     VirtIo           The virtio device for which the mapping is
439
                                  requested.
440
441
  @param[in]     Operation        Indicates if the bus master is going to
442
                                  read or write to system memory.
443
444
  @param[in]     HostAddress      The system memory address to map to shared
445
                                  buffer address.
446
447
  @param[in]     NumberOfBytes    Number of bytes to map.
448
449
  @param[out]    DeviceAddress    The resulting shared map address for the
450
                                  bus master to access the hosts HostAddress.
451
452
  @param[out]    Mapping          A resulting token to pass to
453
                                  VIRTIO_UNMAP_SHARED.
454
455
456
  @retval EFI_SUCCESS             The NumberOfBytes is successfully mapped.
457
  @retval EFI_UNSUPPORTED         The HostAddress cannot be mapped as a
458
                                  common buffer.
459
  @retval EFI_INVALID_PARAMETER   One or more parameters are invalid.
460
  @retval EFI_OUT_OF_RESOURCES    The request could not be completed due to
461
                                  a lack of resources. This includes the case
462
                                  when NumberOfBytes bytes cannot be mapped
463
                                  in one consecutive range.
464
  @retval EFI_DEVICE_ERROR        The system hardware could not map the
465
                                  requested address.
466
**/
467
EFI_STATUS
468
EFIAPI
469
VirtioMapAllBytesInSharedBuffer (
470
  IN  VIRTIO_DEVICE_PROTOCOL  *VirtIo,
471
  IN  VIRTIO_MAP_OPERATION    Operation,
472
  IN  VOID                    *HostAddress,
473
  IN  UINTN                   NumberOfBytes,
474
  OUT EFI_PHYSICAL_ADDRESS    *DeviceAddress,
475
  OUT VOID                    **Mapping
476
  )
477
144
{
478
144
  EFI_STATUS            Status;
479
144
  VOID                  *MapInfo;
480
144
  UINTN                 Size;
481
144
  EFI_PHYSICAL_ADDRESS  PhysicalAddress;
482
483
144
  Size   = NumberOfBytes;
484
144
  Status = VirtIo->MapSharedBuffer (
485
144
                     VirtIo,
486
144
                     Operation,
487
144
                     HostAddress,
488
144
                     &Size,
489
144
                     &PhysicalAddress,
490
144
                     &MapInfo
491
144
                     );
492
144
  if (EFI_ERROR (Status)) {
493
0
    return Status;
494
0
  }
495
496
144
  if (Size < NumberOfBytes) {
497
0
    goto Failed;
498
0
  }
499
500
144
  *Mapping       = MapInfo;
501
144
  *DeviceAddress = PhysicalAddress;
502
503
144
  return EFI_SUCCESS;
504
505
0
Failed:
506
0
  VirtIo->UnmapSharedBuffer (VirtIo, MapInfo);
507
0
  return EFI_OUT_OF_RESOURCES;
508
144
}
509
510
/**
511
512
  Map the ring buffer so that it can be accessed equally by both guest
513
  and hypervisor.
514
515
  @param[in]      VirtIo          The virtio device instance.
516
517
  @param[in]      Ring            The virtio ring to map.
518
519
  @param[out]     RingBaseShift   A resulting translation offset, to be
520
                                  passed to VirtIo->SetQueueAddress().
521
522
  @param[out]     Mapping         A resulting token to pass to
523
                                  VirtIo->UnmapSharedBuffer().
524
525
  @return         Status code from VirtIo->MapSharedBuffer()
526
**/
527
EFI_STATUS
528
EFIAPI
529
VirtioRingMap (
530
  IN  VIRTIO_DEVICE_PROTOCOL  *VirtIo,
531
  IN  VRING                   *Ring,
532
  OUT UINT64                  *RingBaseShift,
533
  OUT VOID                    **Mapping
534
  )
535
144
{
536
144
  EFI_STATUS            Status;
537
144
  EFI_PHYSICAL_ADDRESS  DeviceAddress;
538
539
144
  Status = VirtioMapAllBytesInSharedBuffer (
540
144
             VirtIo,
541
144
             VirtioOperationBusMasterCommonBuffer,
542
144
             Ring->Base,
543
144
             EFI_PAGES_TO_SIZE (Ring->NumPages),
544
144
             &DeviceAddress,
545
144
             Mapping
546
144
             );
547
144
  if (EFI_ERROR (Status)) {
548
0
    return Status;
549
0
  }
550
551
144
  *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base;
552
144
  return EFI_SUCCESS;
553
144
}