3 Utility functions used by virtio device drivers.
5 Copyright (C) 2012, Red Hat, Inc.
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <IndustryStandard/Pci22.h>
18 #include <Library/BaseLib.h>
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
24 #include <Library/VirtioLib.h>
29 Write a word into Region 0 of the device specified by PciIo.
31 Region 0 must be an iomem region. This is an internal function for the
32 driver-specific VIRTIO_CFG_WRITE() macros.
34 @param[in] PciIo Target PCI device.
36 @param[in] FieldOffset Destination offset.
38 @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.
40 @param[in] Value Little endian value to write, converted to UINT64.
41 The least significant FieldSize bytes will be used.
44 @return Status code returned by PciIo->Io.Write().
50 IN EFI_PCI_IO_PROTOCOL
*PciIo
,
57 EFI_PCI_IO_PROTOCOL_WIDTH Width
;
62 Width
= EfiPciIoWidthUint8
;
66 Width
= EfiPciIoWidthUint16
;
74 Width
= EfiPciIoWidthUint32
;
79 return EFI_INVALID_PARAMETER
;
82 return PciIo
->Io
.Write (
95 Read a word from Region 0 of the device specified by PciIo.
97 Region 0 must be an iomem region. This is an internal function for the
98 driver-specific VIRTIO_CFG_READ() macros.
100 @param[in] PciIo Source PCI device.
102 @param[in] FieldOffset Source offset.
104 @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.
106 @param[in] BufferSize Number of bytes available in the target buffer. Must
109 @param[out] Buffer Target buffer.
112 @return Status code returned by PciIo->Io.Read().
118 IN EFI_PCI_IO_PROTOCOL
*PciIo
,
119 IN UINTN FieldOffset
,
126 EFI_PCI_IO_PROTOCOL_WIDTH Width
;
128 ASSERT (FieldSize
== BufferSize
);
133 Width
= EfiPciIoWidthUint8
;
137 Width
= EfiPciIoWidthUint16
;
145 Width
= EfiPciIoWidthUint32
;
150 return EFI_INVALID_PARAMETER
;
153 return PciIo
->Io
.Read (
166 Configure a virtio ring.
168 This function sets up internal storage (the guest-host communication area)
169 and lays out several "navigation" (ie. no-ownership) pointers to parts of
172 Relevant sections from the virtio-0.9.5 spec:
174 - 2.3 Virtqueue Configuration.
176 @param[in] The number of descriptors to allocate for the
177 virtio ring, as requested by the host.
179 @param[out] Ring The virtio ring to set up.
181 @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous
182 pages for the requested QueueSize. Fields of
183 Ring have indeterminate value.
185 @retval EFI_SUCCESS Allocation and setup successful. Ring->Base
186 (and nothing else) is responsible for
198 volatile UINT8
*RingPagesPtr
;
200 RingSize
= ALIGN_VALUE (
201 sizeof *Ring
->Desc
* QueueSize
+
202 sizeof *Ring
->Avail
.Flags
+
203 sizeof *Ring
->Avail
.Idx
+
204 sizeof *Ring
->Avail
.Ring
* QueueSize
+
205 sizeof *Ring
->Avail
.UsedEvent
,
208 RingSize
+= ALIGN_VALUE (
209 sizeof *Ring
->Used
.Flags
+
210 sizeof *Ring
->Used
.Idx
+
211 sizeof *Ring
->Used
.UsedElem
* QueueSize
+
212 sizeof *Ring
->Used
.AvailEvent
,
215 Ring
->NumPages
= EFI_SIZE_TO_PAGES (RingSize
);
216 Ring
->Base
= AllocatePages (Ring
->NumPages
);
217 if (Ring
->Base
== NULL
) {
218 return EFI_OUT_OF_RESOURCES
;
220 SetMem (Ring
->Base
, RingSize
, 0x00);
221 RingPagesPtr
= Ring
->Base
;
223 Ring
->Desc
= (volatile VOID
*) RingPagesPtr
;
224 RingPagesPtr
+= sizeof *Ring
->Desc
* QueueSize
;
226 Ring
->Avail
.Flags
= (volatile VOID
*) RingPagesPtr
;
227 RingPagesPtr
+= sizeof *Ring
->Avail
.Flags
;
229 Ring
->Avail
.Idx
= (volatile VOID
*) RingPagesPtr
;
230 RingPagesPtr
+= sizeof *Ring
->Avail
.Idx
;
232 Ring
->Avail
.Ring
= (volatile VOID
*) RingPagesPtr
;
233 RingPagesPtr
+= sizeof *Ring
->Avail
.Ring
* QueueSize
;
235 Ring
->Avail
.UsedEvent
= (volatile VOID
*) RingPagesPtr
;
236 RingPagesPtr
+= sizeof *Ring
->Avail
.UsedEvent
;
238 RingPagesPtr
= (volatile UINT8
*) Ring
->Base
+
239 ALIGN_VALUE (RingPagesPtr
- (volatile UINT8
*) Ring
->Base
,
242 Ring
->Used
.Flags
= (volatile VOID
*) RingPagesPtr
;
243 RingPagesPtr
+= sizeof *Ring
->Used
.Flags
;
245 Ring
->Used
.Idx
= (volatile VOID
*) RingPagesPtr
;
246 RingPagesPtr
+= sizeof *Ring
->Used
.Idx
;
248 Ring
->Used
.UsedElem
= (volatile VOID
*) RingPagesPtr
;
249 RingPagesPtr
+= sizeof *Ring
->Used
.UsedElem
* QueueSize
;
251 Ring
->Used
.AvailEvent
= (volatile VOID
*) RingPagesPtr
;
252 RingPagesPtr
+= sizeof *Ring
->Used
.AvailEvent
;
254 Ring
->QueueSize
= QueueSize
;
261 Tear down the internal resources of a configured virtio ring.
263 The caller is responsible to stop the host from using this ring before
264 invoking this function: the VSTAT_DRIVER_OK bit must be clear in
267 @param[out] Ring The virtio ring to clean up.
276 FreePages (Ring
->Base
, Ring
->NumPages
);
277 SetMem (Ring
, sizeof *Ring
, 0x00);
283 Turn off interrupt notifications from the host, and prepare for appending
284 multiple descriptors to the virtio ring.
286 The calling driver must be in VSTAT_DRIVER_OK state.
288 @param[in out] Ring The virtio ring we intend to append descriptors to.
290 @param[out] Indices The DESC_INDICES structure to initialize.
297 OUT DESC_INDICES
*Indices
301 // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.
302 // We're going to poll the answer, the host should not send an interrupt.
304 *Ring
->Avail
.Flags
= (UINT16
) VRING_AVAIL_F_NO_INTERRUPT
;
307 // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
309 Indices
->HeadIdx
= *Ring
->Avail
.Idx
;
310 Indices
->NextAvailIdx
= Indices
->HeadIdx
;
316 Append a contiguous buffer for transmission / reception via the virtio ring.
318 This function implements the following sections from virtio-0.9.5:
319 - 2.4.1.1 Placing Buffers into the Descriptor Table
320 - 2.4.1.2 Updating the Available Ring
322 Free space is taken as granted, since the individual drivers support only
323 synchronous requests and host side status is processed in lock-step with
324 request submission. It is the calling driver's responsibility to verify the
325 ring size in advance.
327 The caller is responsible for initializing *Indices with VirtioPrepare()
330 @param[in out] Ring The virtio ring to append the buffer to, as a
333 @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the
334 transmit / receive buffer.
336 @param [in] BufferSize Number of bytes to transmit or receive.
338 @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller
339 computes this mask dependent on further buffers
340 to append and transfer direction.
341 VRING_DESC_F_INDIRECT is unsupported. The
342 VRING_DESC.Next field is always set, but the
343 host only interprets it dependent on
348 @param [in] HeadIdx The index identifying the head buffer (first
349 buffer appended) belonging to this same
352 @param [in out] NextAvailIdx On input, the index identifying the next
353 descriptor available to carry the buffer. On
354 output, incremented by one, modulo 2^16.
361 IN UINTN BufferPhysAddr
,
362 IN UINT32 BufferSize
,
364 IN OUT DESC_INDICES
*Indices
367 volatile VRING_DESC
*Desc
;
369 Desc
= &Ring
->Desc
[Indices
->NextAvailIdx
% Ring
->QueueSize
];
370 Desc
->Addr
= BufferPhysAddr
;
371 Desc
->Len
= BufferSize
;
373 Ring
->Avail
.Ring
[Indices
->NextAvailIdx
++ % Ring
->QueueSize
] =
374 Indices
->HeadIdx
% Ring
->QueueSize
;
375 Desc
->Next
= Indices
->NextAvailIdx
% Ring
->QueueSize
;
381 Notify the host about appended descriptors and wait until it processes the
382 last one (ie. all of them).
384 @param[in] PciIo The target virtio PCI device to notify.
386 @param[in] VirtQueueId Identifies the queue for the target device.
388 @param[in out] Ring The virtio ring with descriptors to submit.
390 @param[in] Indices The function waits until the host processes
391 descriptors up to Indices->NextAvailIdx.
394 @return Error code from VirtioWrite() if it fails.
396 @retval EFI_SUCCESS Otherwise, the host processed all descriptors.
402 IN EFI_PCI_IO_PROTOCOL
*PciIo
,
403 IN UINT16 VirtQueueId
,
405 IN DESC_INDICES
*Indices
409 UINTN PollPeriodUsecs
;
412 // virtio-0.9.5, 2.4.1.3 Updating the Index Field
415 *Ring
->Avail
.Idx
= Indices
->NextAvailIdx
;
418 // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
422 Status
= VirtioWrite (
424 OFFSET_OF (VIRTIO_HDR
, VhdrQueueNotify
),
428 if (EFI_ERROR (Status
)) {
433 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
434 // Wait until the host processes and acknowledges our descriptor chain. The
435 // condition we use for polling is greatly simplified and relies on the
436 // synchronous, lock-step progress.
438 // Keep slowing down until we reach a poll period of slightly above 1 ms.
442 while (*Ring
->Used
.Idx
!= Indices
->NextAvailIdx
) {
443 gBS
->Stall (PollPeriodUsecs
); // calls AcpiTimerLib::MicroSecondDelay
445 if (PollPeriodUsecs
< 1024) {
446 PollPeriodUsecs
*= 2;