Utility functions used by virtio device drivers.\r
\r
Copyright (C) 2012, Red Hat, Inc.\r
+ Portion of Copyright (C) 2013, ARM Ltd.\r
\r
This program and the accompanying materials are licensed and made available\r
under the terms and conditions of the BSD License which accompanies this\r
\r
**/\r
\r
-#include <IndustryStandard/Pci22.h>\r
+#include <Library/BaseLib.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
\r
#include <Library/VirtioLib.h>\r
\r
\r
-/**\r
-\r
- Write a word into Region 0 of the device specified by PciIo.\r
-\r
- Region 0 must be an iomem region. This is an internal function for the\r
- driver-specific VIRTIO_CFG_WRITE() macros.\r
-\r
- @param[in] PciIo Target PCI device.\r
-\r
- @param[in] FieldOffset Destination offset.\r
-\r
- @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.\r
-\r
- @param[in] Value Little endian value to write, converted to UINT64.\r
- The least significant FieldSize bytes will be used.\r
-\r
-\r
- @return Status code returned by PciIo->Io.Write().\r
-\r
-**/\r
-EFIAPI\r
-EFI_STATUS\r
-VirtioWrite (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN UINTN FieldOffset,\r
- IN UINTN FieldSize,\r
- IN UINT64 Value\r
- )\r
-{\r
- UINTN Count;\r
- EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
-\r
- Count = 1;\r
- switch (FieldSize) {\r
- case 1:\r
- Width = EfiPciIoWidthUint8;\r
- break;\r
-\r
- case 2:\r
- Width = EfiPciIoWidthUint16;\r
- break;\r
-\r
- case 8:\r
- Count = 2;\r
- // fall through\r
-\r
- case 4:\r
- Width = EfiPciIoWidthUint32;\r
- break;\r
-\r
- default:\r
- ASSERT (FALSE);\r
- }\r
-\r
- return PciIo->Io.Write (\r
- PciIo,\r
- Width,\r
- PCI_BAR_IDX0,\r
- FieldOffset,\r
- Count,\r
- &Value\r
- );\r
-}\r
-\r
-\r
-/**\r
-\r
- Read a word from Region 0 of the device specified by PciIo.\r
-\r
- Region 0 must be an iomem region. This is an internal function for the\r
- driver-specific VIRTIO_CFG_READ() macros.\r
-\r
- @param[in] PciIo Source PCI device.\r
-\r
- @param[in] FieldOffset Source offset.\r
-\r
- @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.\r
-\r
- @param[in] BufferSize Number of bytes available in the target buffer. Must\r
- equal FieldSize.\r
-\r
- @param[out] Buffer Target buffer.\r
-\r
-\r
- @return Status code returned by PciIo->Io.Read().\r
-\r
-**/\r
-EFIAPI\r
-EFI_STATUS\r
-VirtioRead (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN UINTN FieldOffset,\r
- IN UINTN FieldSize,\r
- IN UINTN BufferSize,\r
- OUT VOID *Buffer\r
- )\r
-{\r
- UINTN Count;\r
- EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
-\r
- ASSERT (FieldSize == BufferSize);\r
-\r
- Count = 1;\r
- switch (FieldSize) {\r
- case 1:\r
- Width = EfiPciIoWidthUint8;\r
- break;\r
-\r
- case 2:\r
- Width = EfiPciIoWidthUint16;\r
- break;\r
-\r
- case 8:\r
- Count = 2;\r
- // fall through\r
-\r
- case 4:\r
- Width = EfiPciIoWidthUint32;\r
- break;\r
-\r
- default:\r
- ASSERT (FALSE);\r
- }\r
-\r
- return PciIo->Io.Read (\r
- PciIo,\r
- Width,\r
- PCI_BAR_IDX0,\r
- FieldOffset,\r
- Count,\r
- Buffer\r
- );\r
-}\r
-\r
-\r
/**\r
\r
Configure a virtio ring.\r
}\r
\r
\r
+/**\r
+\r
+ Turn off interrupt notifications from the host, and prepare for appending\r
+ multiple descriptors to the virtio ring.\r
+\r
+ The calling driver must be in VSTAT_DRIVER_OK state.\r
+\r
+ @param[in,out] Ring The virtio ring we intend to append descriptors to.\r
+\r
+ @param[out] Indices The DESC_INDICES structure to initialize.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+VirtioPrepare (\r
+ IN OUT VRING *Ring,\r
+ OUT DESC_INDICES *Indices\r
+ )\r
+{\r
+ //\r
+ // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.\r
+ // We're going to poll the answer, the host should not send an interrupt.\r
+ //\r
+ *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+\r
+ //\r
+ // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
+ //\r
+ // Since we support only one in-flight descriptor chain, we can always build\r
+ // that chain starting at entry #0 of the descriptor table.\r
+ //\r
+ Indices->HeadDescIdx = 0;\r
+ Indices->NextDescIdx = Indices->HeadDescIdx;\r
+}\r
+\r
+\r
/**\r
\r
Append a contiguous buffer for transmission / reception via the virtio ring.\r
\r
- This function implements the following sections from virtio-0.9.5:\r
+ This function implements the following section from virtio-0.9.5:\r
- 2.4.1.1 Placing Buffers into the Descriptor Table\r
- - 2.4.1.2 Updating the Available Ring\r
\r
Free space is taken as granted, since the individual drivers support only\r
synchronous requests and host side status is processed in lock-step with\r
request submission. It is the calling driver's responsibility to verify the\r
ring size in advance.\r
\r
- @param[in out] Ring The virtio ring to append the buffer to, as a\r
- descriptor.\r
+ The caller is responsible for initializing *Indices with VirtioPrepare()\r
+ first.\r
\r
- @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the\r
- transmit / receive buffer.\r
+ @param[in,out] Ring The virtio ring to append the buffer to, as a\r
+ descriptor.\r
\r
- @param [in] BufferSize Number of bytes to transmit or receive.\r
+ @param[in] BufferPhysAddr (Guest pseudo-physical) start address of the\r
+ transmit / receive buffer.\r
\r
- @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller\r
- computes this mask dependent on further buffers\r
- to append and transfer direction.\r
- VRING_DESC_F_INDIRECT is unsupported. The\r
- VRING_DESC.Next field is always set, but the\r
- host only interprets it dependent on\r
- VRING_DESC_F_NEXT.\r
+ @param[in] BufferSize Number of bytes to transmit or receive.\r
\r
- @param [in] HeadIdx The index identifying the head buffer (first\r
- buffer appended) belonging to this same\r
- request.\r
+ @param[in] Flags A bitmask of VRING_DESC_F_* flags. The caller\r
+ computes this mask dependent on further buffers to\r
+ append and transfer direction.\r
+ VRING_DESC_F_INDIRECT is unsupported. The\r
+ VRING_DESC.Next field is always set, but the host\r
+ only interprets it dependent on VRING_DESC_F_NEXT.\r
\r
- @param [in out] NextAvailIdx On input, the index identifying the next\r
- descriptor available to carry the buffer. On\r
- output, incremented by one, modulo 2^16.\r
+ @param[in,out] Indices Indices->HeadDescIdx is not accessed.\r
+ On input, Indices->NextDescIdx identifies the next\r
+ descriptor to carry the buffer. On output,\r
+ Indices->NextDescIdx is incremented by one, modulo\r
+ 2^16.\r
\r
**/\r
VOID\r
EFIAPI\r
-AppendDesc (\r
- IN OUT VRING *Ring,\r
- IN UINTN BufferPhysAddr,\r
- IN UINT32 BufferSize,\r
- IN UINT16 Flags,\r
- IN UINT16 HeadIdx,\r
- IN OUT UINT16 *NextAvailIdx\r
+VirtioAppendDesc (\r
+ IN OUT VRING *Ring,\r
+ IN UINTN BufferPhysAddr,\r
+ IN UINT32 BufferSize,\r
+ IN UINT16 Flags,\r
+ IN OUT DESC_INDICES *Indices\r
)\r
{\r
volatile VRING_DESC *Desc;\r
\r
- Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];\r
+ Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];\r
Desc->Addr = BufferPhysAddr;\r
Desc->Len = BufferSize;\r
Desc->Flags = Flags;\r
- Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =\r
- HeadIdx % Ring->QueueSize;\r
- Desc->Next = *NextAvailIdx % Ring->QueueSize;\r
+ Desc->Next = Indices->NextDescIdx % Ring->QueueSize;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Notify the host about the descriptor chain just built, and wait until the\r
+ host processes it.\r
+\r
+ @param[in] VirtIo The target virtio device to notify.\r
+\r
+ @param[in] VirtQueueId Identifies the queue for the target device.\r
+\r
+ @param[in,out] Ring The virtio ring with descriptors to submit.\r
+\r
+ @param[in] Indices Indices->NextDescIdx is not accessed.\r
+ Indices->HeadDescIdx identifies the head descriptor\r
+ of the descriptor chain.\r
+\r
+\r
+ @return Error code from VirtIo->SetQueueNotify() if it fails.\r
+\r
+ @retval EFI_SUCCESS Otherwise, the host processed all descriptors.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VirtioFlush (\r
+ IN VIRTIO_DEVICE_PROTOCOL *VirtIo,\r
+ IN UINT16 VirtQueueId,\r
+ IN OUT VRING *Ring,\r
+ IN DESC_INDICES *Indices\r
+ )\r
+{\r
+ UINT16 NextAvailIdx;\r
+ EFI_STATUS Status;\r
+ UINTN PollPeriodUsecs;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.2 Updating the Available Ring\r
+ //\r
+ // It is not exactly clear from the wording of the virtio-0.9.5\r
+ // specification, but each entry in the Available Ring references only the\r
+ // head descriptor of any given descriptor chain.\r
+ //\r
+ NextAvailIdx = *Ring->Avail.Idx;\r
+ Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =\r
+ Indices->HeadDescIdx % Ring->QueueSize;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+ //\r
+ MemoryFence();\r
+ *Ring->Avail.Idx = NextAvailIdx;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
+ // OK.\r
+ //\r
+ MemoryFence();\r
+ Status = VirtIo->SetQueueNotify (VirtIo, VirtQueueId);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
+ // Wait until the host processes and acknowledges our descriptor chain. The\r
+ // condition we use for polling is greatly simplified and relies on the\r
+ // synchronous, lock-step progress.\r
+ //\r
+ // Keep slowing down until we reach a poll period of slightly above 1 ms.\r
+ //\r
+ PollPeriodUsecs = 1;\r
+ MemoryFence();\r
+ while (*Ring->Used.Idx != NextAvailIdx) {\r
+ gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
+\r
+ if (PollPeriodUsecs < 1024) {\r
+ PollPeriodUsecs *= 2;\r
+ }\r
+ MemoryFence();\r
+ }\r
+\r
+ MemoryFence();\r
+ return EFI_SUCCESS;\r
}\r