);\r
\r
\r
+//\r
+// Internal use structure for tracking the submission of a multi-descriptor\r
+// request.\r
+//\r
+typedef struct {\r
+ UINT16 HeadIdx;\r
+ UINT16 NextAvailIdx;\r
+} DESC_INDICES;\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
/**\r
\r
Append a contiguous buffer for transmission / reception via the virtio ring.\r
request submission. It is the calling driver's responsibility to verify the\r
ring size in advance.\r
\r
+ The caller is responsible for initializing *Indices with VirtioPrepare()\r
+ first.\r
+\r
@param[in out] Ring The virtio ring to append the buffer to, as a\r
descriptor.\r
\r
host only interprets it dependent on\r
VRING_DESC_F_NEXT.\r
\r
+ In *Indices:\r
+\r
@param [in] HeadIdx The index identifying the head buffer (first\r
buffer appended) belonging to this same\r
request.\r
VOID\r
EFIAPI\r
VirtioAppendDesc (\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
+ 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
+\r
+/**\r
+\r
+ Notify the host about appended descriptors and wait until it processes the\r
+ last one (ie. all of them).\r
+\r
+ @param[in] PciIo The target virtio PCI 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 The function waits until the host processes\r
+ descriptors up to Indices->NextAvailIdx.\r
+\r
+\r
+ @return Error code from VirtioWrite() 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 EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT16 VirtQueueId,\r
+ IN OUT VRING *Ring,\r
+ IN DESC_INDICES *Indices\r
);\r
\r
#endif // _VIRTIO_LIB_H_\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
+/**\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
+ Indices->HeadIdx = *Ring->Avail.Idx;\r
+ Indices->NextAvailIdx = Indices->HeadIdx;\r
+}\r
+\r
+\r
/**\r
\r
Append a contiguous buffer for transmission / reception via the virtio ring.\r
request submission. It is the calling driver's responsibility to verify the\r
ring size in advance.\r
\r
+ The caller is responsible for initializing *Indices with VirtioPrepare()\r
+ first.\r
+\r
@param[in out] Ring The virtio ring to append the buffer to, as a\r
descriptor.\r
\r
host only interprets it dependent on\r
VRING_DESC_F_NEXT.\r
\r
+ In *Indices:\r
+\r
@param [in] HeadIdx The index identifying the head buffer (first\r
buffer appended) belonging to this same\r
request.\r
VOID\r
EFIAPI\r
VirtioAppendDesc (\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
+ 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->NextAvailIdx % 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
+ Ring->Avail.Ring[Indices->NextAvailIdx++ % Ring->QueueSize] =\r
+ Indices->HeadIdx % Ring->QueueSize;\r
+ Desc->Next = Indices->NextAvailIdx % Ring->QueueSize;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Notify the host about appended descriptors and wait until it processes the\r
+ last one (ie. all of them).\r
+\r
+ @param[in] PciIo The target virtio PCI 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 The function waits until the host processes\r
+ descriptors up to Indices->NextAvailIdx.\r
+\r
+\r
+ @return Error code from VirtioWrite() 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 EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN UINT16 VirtQueueId,\r
+ IN OUT VRING *Ring,\r
+ IN DESC_INDICES *Indices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN PollPeriodUsecs;\r
+\r
+ //\r
+ // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+ //\r
+ MemoryFence();\r
+ *Ring->Avail.Idx = Indices->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 = VirtioWrite (\r
+ PciIo,\r
+ OFFSET_OF (VIRTIO_HDR, VhdrQueueNotify),\r
+ sizeof (UINT16),\r
+ VirtQueueId\r
+ );\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 != Indices->NextAvailIdx) {\r
+ gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
+\r
+ if (PollPeriodUsecs < 1024) {\r
+ PollPeriodUsecs *= 2;\r
+ }\r
+ MemoryFence();\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
}\r
OvmfPkg/OvmfPkg.dec\r
\r
[LibraryClasses]\r
+ BaseLib\r
BaseMemoryLib\r
DebugLib\r
MemoryAllocationLib\r
+ UefiBootServicesTableLib\r
UINT32 BlockSize;\r
volatile VIRTIO_BLK_REQ Request;\r
volatile UINT8 HostStatus;\r
- UINT16 FirstAvailIdx;\r
- UINT16 NextAvailIdx;\r
- UINTN PollPeriodUsecs;\r
+ DESC_INDICES Indices;\r
\r
BlockSize = Dev->BlockIoMedia.BlockSize;\r
\r
Request.IoPrio = 0;\r
Request.Sector = Lba * (BlockSize / 512);\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
- *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
+ VirtioPrepare (&Dev->Ring, &Indices);\r
\r
//\r
// preset a host status for ourselves that we do not accept as success\r
//\r
ASSERT (Dev->Ring.QueueSize >= 3);\r
\r
- //\r
- // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
- //\r
- FirstAvailIdx = *Dev->Ring.Avail.Idx;\r
- NextAvailIdx = FirstAvailIdx;\r
-\r
//\r
// virtio-blk header in first desc\r
//\r
VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,\r
- VRING_DESC_F_NEXT, FirstAvailIdx, &NextAvailIdx);\r
+ VRING_DESC_F_NEXT, &Indices);\r
\r
//\r
// data buffer for read/write in second desc\r
//\r
VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,\r
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),\r
- FirstAvailIdx, &NextAvailIdx);\r
+ &Indices);\r
}\r
\r
//\r
// host status in last (second or third) desc\r
//\r
VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,\r
- VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx);\r
+ VRING_DESC_F_WRITE, &Indices);\r
\r
//\r
- // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
+ // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
//\r
- MemoryFence();\r
- *Dev->Ring.Avail.Idx = NextAvailIdx;\r
-\r
- //\r
- // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
- // OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).\r
- //\r
- MemoryFence();\r
- if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, 0))) {\r
- return EFI_DEVICE_ERROR;\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 3-part descriptor\r
- // chain. The condition we use for polling is greatly simplified and relies\r
- // on synchronous, the 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 (*Dev->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
- if (HostStatus == VIRTIO_BLK_S_OK) {\r
+ if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&\r
+ HostStatus == VIRTIO_BLK_S_OK) {\r
return EFI_SUCCESS;\r
}\r
\r