TianoCore EDK2 master
Loading...
Searching...
No Matches
VirtioLib.c
Go to the documentation of this file.
1
13#include <Library/BaseLib.h>
15#include <Library/DebugLib.h>
17
18#include <Library/VirtioLib.h>
19
48EFIAPI
51 IN UINT16 QueueSize,
52 OUT VRING *Ring
53 )
54{
55 EFI_STATUS Status;
56 UINTN RingSize;
57 volatile UINT8 *RingPagesPtr;
58
59 RingSize = ALIGN_VALUE (
60 sizeof *Ring->Desc * QueueSize +
61 sizeof *Ring->Avail.Flags +
62 sizeof *Ring->Avail.Idx +
63 sizeof *Ring->Avail.Ring * QueueSize +
64 sizeof *Ring->Avail.UsedEvent,
65 EFI_PAGE_SIZE
66 );
67
68 RingSize += ALIGN_VALUE (
69 sizeof *Ring->Used.Flags +
70 sizeof *Ring->Used.Idx +
71 sizeof *Ring->Used.UsedElem * QueueSize +
72 sizeof *Ring->Used.AvailEvent,
73 EFI_PAGE_SIZE
74 );
75
76 //
77 // Allocate a shared ring buffer
78 //
79 Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);
80 Status = VirtIo->AllocateSharedPages (
81 VirtIo,
82 Ring->NumPages,
83 &Ring->Base
84 );
85 if (EFI_ERROR (Status)) {
86 return Status;
87 }
88
89 SetMem (Ring->Base, RingSize, 0x00);
90 RingPagesPtr = Ring->Base;
91
92 Ring->Desc = (volatile VOID *)RingPagesPtr;
93 RingPagesPtr += sizeof *Ring->Desc * QueueSize;
94
95 Ring->Avail.Flags = (volatile VOID *)RingPagesPtr;
96 RingPagesPtr += sizeof *Ring->Avail.Flags;
97
98 Ring->Avail.Idx = (volatile VOID *)RingPagesPtr;
99 RingPagesPtr += sizeof *Ring->Avail.Idx;
100
101 Ring->Avail.Ring = (volatile VOID *)RingPagesPtr;
102 RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;
103
104 Ring->Avail.UsedEvent = (volatile VOID *)RingPagesPtr;
105 RingPagesPtr += sizeof *Ring->Avail.UsedEvent;
106
107 RingPagesPtr = (volatile UINT8 *)Ring->Base +
109 RingPagesPtr - (volatile UINT8 *)Ring->Base,
110 EFI_PAGE_SIZE
111 );
112
113 Ring->Used.Flags = (volatile VOID *)RingPagesPtr;
114 RingPagesPtr += sizeof *Ring->Used.Flags;
115
116 Ring->Used.Idx = (volatile VOID *)RingPagesPtr;
117 RingPagesPtr += sizeof *Ring->Used.Idx;
118
119 Ring->Used.UsedElem = (volatile VOID *)RingPagesPtr;
120 RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;
121
122 Ring->Used.AvailEvent = (volatile VOID *)RingPagesPtr;
123 RingPagesPtr += sizeof *Ring->Used.AvailEvent;
124
125 Ring->QueueSize = QueueSize;
126 return EFI_SUCCESS;
127}
128
142VOID
143EFIAPI
146 IN OUT VRING *Ring
147 )
148{
149 VirtIo->FreeSharedPages (VirtIo, Ring->NumPages, Ring->Base);
150 SetMem (Ring, sizeof *Ring, 0x00);
151}
152
165VOID
166EFIAPI
168 IN OUT VRING *Ring,
169 OUT DESC_INDICES *Indices
170 )
171{
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 *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 Indices->HeadDescIdx = 0;
185 Indices->NextDescIdx = Indices->HeadDescIdx;
186}
187
226VOID
227EFIAPI
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{
236 volatile VRING_DESC *Desc;
237
238 Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];
239 Desc->Addr = BufferDeviceAddress;
240 Desc->Len = BufferSize;
241 Desc->Flags = Flags;
242 Desc->Next = Indices->NextDescIdx % Ring->QueueSize;
243}
244
273EFIAPI
276 IN UINT16 VirtQueueId,
277 IN OUT VRING *Ring,
278 IN DESC_INDICES *Indices,
279 OUT UINT32 *UsedLen OPTIONAL
280 )
281{
282 UINT16 NextAvailIdx;
283 UINT16 LastUsedIdx;
284 EFI_STATUS Status;
285 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 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 LastUsedIdx = NextAvailIdx;
300 Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =
301 Indices->HeadDescIdx % Ring->QueueSize;
302
303 //
304 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
305 //
306 MemoryFence ();
307 *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 MemoryFence ();
314 Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);
315 if (EFI_ERROR (Status)) {
316 return Status;
317 }
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 PollPeriodUsecs = 1;
328 MemoryFence ();
329 while (*Ring->Used.Idx != NextAvailIdx) {
330 gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay
331
332 if (PollPeriodUsecs < 1024) {
333 PollPeriodUsecs *= 2;
334 }
335
336 MemoryFence ();
337 }
338
339 MemoryFence ();
340
341 if (UsedLen != NULL) {
342 volatile CONST VRING_USED_ELEM *UsedElem;
343
344 UsedElem = &Ring->Used.UsedElem[LastUsedIdx % Ring->QueueSize];
345 ASSERT (UsedElem->Id == Indices->HeadDescIdx);
346 *UsedLen = UsedElem->Len;
347 }
348
349 return EFI_SUCCESS;
350}
351
390EFIAPI
393 IN UINT64 Features,
394 IN OUT UINT8 *DeviceStatus
395 )
396{
397 EFI_STATUS Status;
398
399 if (VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
400 return EFI_UNSUPPORTED;
401 }
402
403 Status = VirtIo->SetGuestFeatures (VirtIo, Features);
404 if (EFI_ERROR (Status)) {
405 return Status;
406 }
407
408 *DeviceStatus |= VSTAT_FEATURES_OK;
409 Status = VirtIo->SetDeviceStatus (VirtIo, *DeviceStatus);
410 if (EFI_ERROR (Status)) {
411 return Status;
412 }
413
414 Status = VirtIo->GetDeviceStatus (VirtIo, DeviceStatus);
415 if (EFI_ERROR (Status)) {
416 return Status;
417 }
418
419 if ((*DeviceStatus & VSTAT_FEATURES_OK) == 0) {
420 Status = EFI_UNSUPPORTED;
421 }
422
423 return Status;
424}
425
468EFIAPI
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{
478 EFI_STATUS Status;
479 VOID *MapInfo;
480 UINTN Size;
481 EFI_PHYSICAL_ADDRESS PhysicalAddress;
482
483 Size = NumberOfBytes;
484 Status = VirtIo->MapSharedBuffer (
485 VirtIo,
486 Operation,
487 HostAddress,
488 &Size,
489 &PhysicalAddress,
490 &MapInfo
491 );
492 if (EFI_ERROR (Status)) {
493 return Status;
494 }
495
496 if (Size < NumberOfBytes) {
497 goto Failed;
498 }
499
500 *Mapping = MapInfo;
501 *DeviceAddress = PhysicalAddress;
502
503 return EFI_SUCCESS;
504
505Failed:
506 VirtIo->UnmapSharedBuffer (VirtIo, MapInfo);
507 return EFI_OUT_OF_RESOURCES;
508}
509
528EFIAPI
531 IN VRING *Ring,
532 OUT UINT64 *RingBaseShift,
533 OUT VOID **Mapping
534 )
535{
536 EFI_STATUS Status;
537 EFI_PHYSICAL_ADDRESS DeviceAddress;
538
540 VirtIo,
541 VirtioOperationBusMasterCommonBuffer,
542 Ring->Base,
543 EFI_PAGES_TO_SIZE (Ring->NumPages),
544 &DeviceAddress,
545 Mapping
546 );
547 if (EFI_ERROR (Status)) {
548 return Status;
549 }
550
551 *RingBaseShift = DeviceAddress - (UINT64)(UINTN)Ring->Base;
552 return EFI_SUCCESS;
553}
UINT64 UINTN
VOID EFIAPI MemoryFence(VOID)
Definition: CpuBreakpoint.c:42
VOID *EFIAPI SetMem(OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value)
Definition: SetMemWrapper.c:38
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define ALIGN_VALUE(Value, Alignment)
Definition: Base.h:948
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
UINT64 EFI_PHYSICAL_ADDRESS
Definition: UefiBaseType.h:50
#define EFI_PAGES_TO_SIZE(Pages)
Definition: UefiBaseType.h:213
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SIZE_TO_PAGES(Size)
Definition: UefiBaseType.h:200
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
EFI_STATUS EFIAPI VirtioMapAllBytesInSharedBuffer(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN VIRTIO_MAP_OPERATION Operation, IN VOID *HostAddress, IN UINTN NumberOfBytes, OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, OUT VOID **Mapping)
Definition: VirtioLib.c:469
VOID EFIAPI VirtioAppendDesc(IN OUT VRING *Ring, IN UINT64 BufferDeviceAddress, IN UINT32 BufferSize, IN UINT16 Flags, IN OUT DESC_INDICES *Indices)
Definition: VirtioLib.c:228
EFI_STATUS EFIAPI VirtioFlush(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN UINT16 VirtQueueId, IN OUT VRING *Ring, IN DESC_INDICES *Indices, OUT UINT32 *UsedLen OPTIONAL)
Definition: VirtioLib.c:274
EFI_STATUS EFIAPI VirtioRingMap(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN VRING *Ring, OUT UINT64 *RingBaseShift, OUT VOID **Mapping)
Definition: VirtioLib.c:529
EFI_STATUS EFIAPI Virtio10WriteFeatures(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN UINT64 Features, IN OUT UINT8 *DeviceStatus)
Definition: VirtioLib.c:391
VOID EFIAPI VirtioPrepare(IN OUT VRING *Ring, OUT DESC_INDICES *Indices)
Definition: VirtioLib.c:167
EFI_STATUS EFIAPI VirtioRingInit(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN UINT16 QueueSize, OUT VRING *Ring)
Definition: VirtioLib.c:49
VOID EFIAPI VirtioRingUninit(IN VIRTIO_DEVICE_PROTOCOL *VirtIo, IN OUT VRING *Ring)
Definition: VirtioLib.c:144