3 Utility functions used by virtio device drivers.
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>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/VirtioLib.h>
22 Configure a virtio ring.
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
28 Relevant sections from the virtio-0.9.5 spec:
30 - 2.3 Virtqueue Configuration.
32 @param[in] VirtIo The virtio device which will use the ring.
34 @param[in] The number of descriptors to allocate for the
35 virtio ring, as requested by the host.
37 @param[out] Ring The virtio ring to set up.
39 @return Status codes propagated from
40 VirtIo->AllocateSharedPages().
42 @retval EFI_SUCCESS Allocation and setup successful. Ring->Base
43 (and nothing else) is responsible for
50 IN VIRTIO_DEVICE_PROTOCOL
*VirtIo
,
57 volatile UINT8
*RingPagesPtr
;
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
,
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
,
77 // Allocate a shared ring buffer
79 Ring
->NumPages
= EFI_SIZE_TO_PAGES (RingSize
);
80 Status
= VirtIo
->AllocateSharedPages (
85 if (EFI_ERROR (Status
)) {
89 SetMem (Ring
->Base
, RingSize
, 0x00);
90 RingPagesPtr
= Ring
->Base
;
92 Ring
->Desc
= (volatile VOID
*)RingPagesPtr
;
93 RingPagesPtr
+= sizeof *Ring
->Desc
* QueueSize
;
95 Ring
->Avail
.Flags
= (volatile VOID
*)RingPagesPtr
;
96 RingPagesPtr
+= sizeof *Ring
->Avail
.Flags
;
98 Ring
->Avail
.Idx
= (volatile VOID
*)RingPagesPtr
;
99 RingPagesPtr
+= sizeof *Ring
->Avail
.Idx
;
101 Ring
->Avail
.Ring
= (volatile VOID
*)RingPagesPtr
;
102 RingPagesPtr
+= sizeof *Ring
->Avail
.Ring
* QueueSize
;
104 Ring
->Avail
.UsedEvent
= (volatile VOID
*)RingPagesPtr
;
105 RingPagesPtr
+= sizeof *Ring
->Avail
.UsedEvent
;
107 RingPagesPtr
= (volatile UINT8
*)Ring
->Base
+
109 RingPagesPtr
- (volatile UINT8
*)Ring
->Base
,
113 Ring
->Used
.Flags
= (volatile VOID
*)RingPagesPtr
;
114 RingPagesPtr
+= sizeof *Ring
->Used
.Flags
;
116 Ring
->Used
.Idx
= (volatile VOID
*)RingPagesPtr
;
117 RingPagesPtr
+= sizeof *Ring
->Used
.Idx
;
119 Ring
->Used
.UsedElem
= (volatile VOID
*)RingPagesPtr
;
120 RingPagesPtr
+= sizeof *Ring
->Used
.UsedElem
* QueueSize
;
122 Ring
->Used
.AvailEvent
= (volatile VOID
*)RingPagesPtr
;
123 RingPagesPtr
+= sizeof *Ring
->Used
.AvailEvent
;
125 Ring
->QueueSize
= QueueSize
;
131 Tear down the internal resources of a configured virtio ring.
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
137 @param[in] VirtIo The virtio device which was using the ring.
139 @param[out] Ring The virtio ring to clean up.
145 IN VIRTIO_DEVICE_PROTOCOL
*VirtIo
,
149 VirtIo
->FreeSharedPages (VirtIo
, Ring
->NumPages
, Ring
->Base
);
150 SetMem (Ring
, sizeof *Ring
, 0x00);
155 Turn off interrupt notifications from the host, and prepare for appending
156 multiple descriptors to the virtio ring.
158 The calling driver must be in VSTAT_DRIVER_OK state.
160 @param[in,out] Ring The virtio ring we intend to append descriptors to.
162 @param[out] Indices The DESC_INDICES structure to initialize.
169 OUT DESC_INDICES
*Indices
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.
176 *Ring
->Avail
.Flags
= (UINT16
)VRING_AVAIL_F_NO_INTERRUPT
;
179 // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
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.
184 Indices
->HeadDescIdx
= 0;
185 Indices
->NextDescIdx
= Indices
->HeadDescIdx
;
190 Append a contiguous buffer for transmission / reception via the virtio ring.
192 This function implements the following section from virtio-0.9.5:
193 - 2.4.1.1 Placing Buffers into the Descriptor Table
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.
200 The caller is responsible for initializing *Indices with VirtioPrepare()
203 @param[in,out] Ring The virtio ring to append the buffer to,
206 @param[in] BufferDeviceAddress (Bus master device) start address of the
207 transmit / receive buffer.
209 @param[in] BufferSize Number of bytes to transmit or receive.
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.
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.
230 IN UINT64 BufferDeviceAddress
,
231 IN UINT32 BufferSize
,
233 IN OUT DESC_INDICES
*Indices
236 volatile VRING_DESC
*Desc
;
238 Desc
= &Ring
->Desc
[Indices
->NextDescIdx
++ % Ring
->QueueSize
];
239 Desc
->Addr
= BufferDeviceAddress
;
240 Desc
->Len
= BufferSize
;
242 Desc
->Next
= Indices
->NextDescIdx
% Ring
->QueueSize
;
247 Notify the host about the descriptor chain just built, and wait until the
250 @param[in] VirtIo The target virtio device to notify.
252 @param[in] VirtQueueId Identifies the queue for the target device.
254 @param[in,out] Ring The virtio ring with descriptors to submit.
256 @param[in] Indices Indices->NextDescIdx is not accessed.
257 Indices->HeadDescIdx identifies the head descriptor
258 of the descriptor chain.
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
267 @return Error code from VirtIo->SetQueueNotify() if it fails.
269 @retval EFI_SUCCESS Otherwise, the host processed all descriptors.
275 IN VIRTIO_DEVICE_PROTOCOL
*VirtIo
,
276 IN UINT16 VirtQueueId
,
278 IN DESC_INDICES
*Indices
,
279 OUT UINT32
*UsedLen OPTIONAL
285 UINTN PollPeriodUsecs
;
288 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring
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.
294 NextAvailIdx
= *Ring
->Avail
.Idx
;
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.)
299 LastUsedIdx
= NextAvailIdx
;
300 Ring
->Avail
.Ring
[NextAvailIdx
++ % Ring
->QueueSize
] =
301 Indices
->HeadDescIdx
% Ring
->QueueSize
;
304 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
307 *Ring
->Avail
.Idx
= NextAvailIdx
;
310 // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
314 Status
= VirtIo
->SetQueueNotify (VirtIo
, VirtQueueId
);
315 if (EFI_ERROR (Status
)) {
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.
325 // Keep slowing down until we reach a poll period of slightly above 1 ms.
329 while (*Ring
->Used
.Idx
!= NextAvailIdx
) {
330 gBS
->Stall (PollPeriodUsecs
); // calls AcpiTimerLib::MicroSecondDelay
332 if (PollPeriodUsecs
< 1024) {
333 PollPeriodUsecs
*= 2;
341 if (UsedLen
!= NULL
) {
342 volatile CONST VRING_USED_ELEM
*UsedElem
;
344 UsedElem
= &Ring
->Used
.UsedElem
[LastUsedIdx
% Ring
->QueueSize
];
345 ASSERT (UsedElem
->Id
== Indices
->HeadDescIdx
);
346 *UsedLen
= UsedElem
->Len
;
354 Report the feature bits to the VirtIo 1.0 device that the VirtIo 1.0 driver
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.
363 @param[in] VirtIo Report feature bits to this device.
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().
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
378 @retval EFI_SUCCESS The device accepted the configuration in Features.
380 @return EFI_UNSUPPORTED The device rejected the configuration in Features.
382 @retval EFI_UNSUPPORTED VirtIo->Revision is smaller than 1.0.0.
384 @return Error codes from the SetGuestFeatures(),
385 SetDeviceStatus(), GetDeviceStatus() member
391 Virtio10WriteFeatures (
392 IN VIRTIO_DEVICE_PROTOCOL
*VirtIo
,
394 IN OUT UINT8
*DeviceStatus
399 if (VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
400 return EFI_UNSUPPORTED
;
403 Status
= VirtIo
->SetGuestFeatures (VirtIo
, Features
);
404 if (EFI_ERROR (Status
)) {
408 *DeviceStatus
|= VSTAT_FEATURES_OK
;
409 Status
= VirtIo
->SetDeviceStatus (VirtIo
, *DeviceStatus
);
410 if (EFI_ERROR (Status
)) {
414 Status
= VirtIo
->GetDeviceStatus (VirtIo
, DeviceStatus
);
415 if (EFI_ERROR (Status
)) {
419 if ((*DeviceStatus
& VSTAT_FEATURES_OK
) == 0) {
420 Status
= EFI_UNSUPPORTED
;
427 Provides the virtio device address required to access system memory from a
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)
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
438 @param[in] VirtIo The virtio device for which the mapping is
441 @param[in] Operation Indicates if the bus master is going to
442 read or write to system memory.
444 @param[in] HostAddress The system memory address to map to shared
447 @param[in] NumberOfBytes Number of bytes to map.
449 @param[out] DeviceAddress The resulting shared map address for the
450 bus master to access the hosts HostAddress.
452 @param[out] Mapping A resulting token to pass to
456 @retval EFI_SUCCESS The NumberOfBytes is successfully mapped.
457 @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a
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
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
,
481 EFI_PHYSICAL_ADDRESS PhysicalAddress
;
483 Size
= NumberOfBytes
;
484 Status
= VirtIo
->MapSharedBuffer (
492 if (EFI_ERROR (Status
)) {
496 if (Size
< NumberOfBytes
) {
501 *DeviceAddress
= PhysicalAddress
;
506 VirtIo
->UnmapSharedBuffer (VirtIo
, MapInfo
);
507 return EFI_OUT_OF_RESOURCES
;
512 Map the ring buffer so that it can be accessed equally by both guest
515 @param[in] VirtIo The virtio device instance.
517 @param[in] Ring The virtio ring to map.
519 @param[out] RingBaseShift A resulting translation offset, to be
520 passed to VirtIo->SetQueueAddress().
522 @param[out] Mapping A resulting token to pass to
523 VirtIo->UnmapSharedBuffer().
525 @return Status code from VirtIo->MapSharedBuffer()
530 IN VIRTIO_DEVICE_PROTOCOL
*VirtIo
,
532 OUT UINT64
*RingBaseShift
,
537 EFI_PHYSICAL_ADDRESS DeviceAddress
;
539 Status
= VirtioMapAllBytesInSharedBuffer (
541 VirtioOperationBusMasterCommonBuffer
,
543 EFI_PAGES_TO_SIZE (Ring
->NumPages
),
547 if (EFI_ERROR (Status
)) {
551 *RingBaseShift
= DeviceAddress
- (UINT64
)(UINTN
)Ring
->Base
;