From e371e7e545b266ce4ae8ff76da6c98616214599f Mon Sep 17 00:00:00 2001 From: jljusten Date: Fri, 12 Oct 2012 18:54:35 +0000 Subject: [PATCH] OvmfPkg: librarize reusable bits from VirtioBlkDxe's SynchronousRequest() new VirtioLib functions: - VirtioPrepare(): prepare for appending descriptors - VirtioFlush(): submit descriptor chain and await host answer Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Jordan Justen git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13844 6f19259b-4bc3-4df7-8a09-765794883524 --- OvmfPkg/Include/Library/VirtioLib.h | 76 +++++++++++-- OvmfPkg/Library/VirtioLib/VirtioLib.c | 135 ++++++++++++++++++++++-- OvmfPkg/Library/VirtioLib/VirtioLib.inf | 2 + OvmfPkg/VirtioBlkDxe/VirtioBlk.c | 58 ++-------- 4 files changed, 205 insertions(+), 66 deletions(-) diff --git a/OvmfPkg/Include/Library/VirtioLib.h b/OvmfPkg/Include/Library/VirtioLib.h index eff66da6b3..07e4add784 100644 --- a/OvmfPkg/Include/Library/VirtioLib.h +++ b/OvmfPkg/Include/Library/VirtioLib.h @@ -135,6 +135,36 @@ VirtioRingUninit ( ); +// +// Internal use structure for tracking the submission of a multi-descriptor +// request. +// +typedef struct { + UINT16 HeadIdx; + UINT16 NextAvailIdx; +} DESC_INDICES; + + +/** + + Turn off interrupt notifications from the host, and prepare for appending + multiple descriptors to the virtio ring. + + The calling driver must be in VSTAT_DRIVER_OK state. + + @param[in out] Ring The virtio ring we intend to append descriptors to. + + @param[out] Indices The DESC_INDICES structure to initialize. + +**/ +VOID +EFIAPI +VirtioPrepare ( + IN OUT VRING *Ring, + OUT DESC_INDICES *Indices + ); + + /** Append a contiguous buffer for transmission / reception via the virtio ring. @@ -148,6 +178,9 @@ VirtioRingUninit ( request submission. It is the calling driver's responsibility to verify the ring size in advance. + The caller is responsible for initializing *Indices with VirtioPrepare() + first. + @param[in out] Ring The virtio ring to append the buffer to, as a descriptor. @@ -164,6 +197,8 @@ VirtioRingUninit ( host only interprets it dependent on VRING_DESC_F_NEXT. + In *Indices: + @param [in] HeadIdx The index identifying the head buffer (first buffer appended) belonging to this same request. @@ -176,12 +211,41 @@ VirtioRingUninit ( VOID EFIAPI VirtioAppendDesc ( - IN OUT VRING *Ring, - IN UINTN BufferPhysAddr, - IN UINT32 BufferSize, - IN UINT16 Flags, - IN UINT16 HeadIdx, - IN OUT UINT16 *NextAvailIdx + IN OUT VRING *Ring, + IN UINTN BufferPhysAddr, + IN UINT32 BufferSize, + IN UINT16 Flags, + IN OUT DESC_INDICES *Indices + ); + + +/** + + Notify the host about appended descriptors and wait until it processes the + last one (ie. all of them). + + @param[in] PciIo The target virtio PCI device to notify. + + @param[in] VirtQueueId Identifies the queue for the target device. + + @param[in out] Ring The virtio ring with descriptors to submit. + + @param[in] Indices The function waits until the host processes + descriptors up to Indices->NextAvailIdx. + + + @return Error code from VirtioWrite() if it fails. + + @retval EFI_SUCCESS Otherwise, the host processed all descriptors. + +**/ +EFI_STATUS +EFIAPI +VirtioFlush ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 VirtQueueId, + IN OUT VRING *Ring, + IN DESC_INDICES *Indices ); #endif // _VIRTIO_LIB_H_ diff --git a/OvmfPkg/Library/VirtioLib/VirtioLib.c b/OvmfPkg/Library/VirtioLib/VirtioLib.c index f0328ef4cd..6ed977cd10 100644 --- a/OvmfPkg/Library/VirtioLib/VirtioLib.c +++ b/OvmfPkg/Library/VirtioLib/VirtioLib.c @@ -15,9 +15,11 @@ **/ #include +#include #include #include #include +#include #include @@ -274,6 +276,39 @@ VirtioRingUninit ( } +/** + + Turn off interrupt notifications from the host, and prepare for appending + multiple descriptors to the virtio ring. + + The calling driver must be in VSTAT_DRIVER_OK state. + + @param[in out] Ring The virtio ring we intend to append descriptors to. + + @param[out] Indices The DESC_INDICES structure to initialize. + +**/ +VOID +EFIAPI +VirtioPrepare ( + IN OUT VRING *Ring, + OUT DESC_INDICES *Indices + ) +{ + // + // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device. + // We're going to poll the answer, the host should not send an interrupt. + // + *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT; + + // + // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device. + // + Indices->HeadIdx = *Ring->Avail.Idx; + Indices->NextAvailIdx = Indices->HeadIdx; +} + + /** Append a contiguous buffer for transmission / reception via the virtio ring. @@ -287,6 +322,9 @@ VirtioRingUninit ( request submission. It is the calling driver's responsibility to verify the ring size in advance. + The caller is responsible for initializing *Indices with VirtioPrepare() + first. + @param[in out] Ring The virtio ring to append the buffer to, as a descriptor. @@ -303,6 +341,8 @@ VirtioRingUninit ( host only interprets it dependent on VRING_DESC_F_NEXT. + In *Indices: + @param [in] HeadIdx The index identifying the head buffer (first buffer appended) belonging to this same request. @@ -315,21 +355,96 @@ VirtioRingUninit ( VOID EFIAPI VirtioAppendDesc ( - IN OUT VRING *Ring, - IN UINTN BufferPhysAddr, - IN UINT32 BufferSize, - IN UINT16 Flags, - IN UINT16 HeadIdx, - IN OUT UINT16 *NextAvailIdx + IN OUT VRING *Ring, + IN UINTN BufferPhysAddr, + IN UINT32 BufferSize, + IN UINT16 Flags, + IN OUT DESC_INDICES *Indices ) { volatile VRING_DESC *Desc; - Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize]; + Desc = &Ring->Desc[Indices->NextAvailIdx % Ring->QueueSize]; Desc->Addr = BufferPhysAddr; Desc->Len = BufferSize; Desc->Flags = Flags; - Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] = - HeadIdx % Ring->QueueSize; - Desc->Next = *NextAvailIdx % Ring->QueueSize; + Ring->Avail.Ring[Indices->NextAvailIdx++ % Ring->QueueSize] = + Indices->HeadIdx % Ring->QueueSize; + Desc->Next = Indices->NextAvailIdx % Ring->QueueSize; +} + + +/** + + Notify the host about appended descriptors and wait until it processes the + last one (ie. all of them). + + @param[in] PciIo The target virtio PCI device to notify. + + @param[in] VirtQueueId Identifies the queue for the target device. + + @param[in out] Ring The virtio ring with descriptors to submit. + + @param[in] Indices The function waits until the host processes + descriptors up to Indices->NextAvailIdx. + + + @return Error code from VirtioWrite() if it fails. + + @retval EFI_SUCCESS Otherwise, the host processed all descriptors. + +**/ +EFI_STATUS +EFIAPI +VirtioFlush ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT16 VirtQueueId, + IN OUT VRING *Ring, + IN DESC_INDICES *Indices + ) +{ + EFI_STATUS Status; + UINTN PollPeriodUsecs; + + // + // virtio-0.9.5, 2.4.1.3 Updating the Index Field + // + MemoryFence(); + *Ring->Avail.Idx = Indices->NextAvailIdx; + + // + // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are + // OK. + // + MemoryFence(); + Status = VirtioWrite ( + PciIo, + OFFSET_OF (VIRTIO_HDR, VhdrQueueNotify), + sizeof (UINT16), + VirtQueueId + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device + // Wait until the host processes and acknowledges our descriptor chain. The + // condition we use for polling is greatly simplified and relies on the + // synchronous, lock-step progress. + // + // Keep slowing down until we reach a poll period of slightly above 1 ms. + // + PollPeriodUsecs = 1; + MemoryFence(); + while (*Ring->Used.Idx != Indices->NextAvailIdx) { + gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay + + if (PollPeriodUsecs < 1024) { + PollPeriodUsecs *= 2; + } + MemoryFence(); + } + + return EFI_SUCCESS; } diff --git a/OvmfPkg/Library/VirtioLib/VirtioLib.inf b/OvmfPkg/Library/VirtioLib/VirtioLib.inf index cfd93a2c2c..fb5897a88e 100644 --- a/OvmfPkg/Library/VirtioLib/VirtioLib.inf +++ b/OvmfPkg/Library/VirtioLib/VirtioLib.inf @@ -29,6 +29,8 @@ OvmfPkg/OvmfPkg.dec [LibraryClasses] + BaseLib BaseMemoryLib DebugLib MemoryAllocationLib + UefiBootServicesTableLib diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c index ce38ff7093..44a05cfb5f 100644 --- a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c +++ b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c @@ -248,9 +248,7 @@ SynchronousRequest ( UINT32 BlockSize; volatile VIRTIO_BLK_REQ Request; volatile UINT8 HostStatus; - UINT16 FirstAvailIdx; - UINT16 NextAvailIdx; - UINTN PollPeriodUsecs; + DESC_INDICES Indices; BlockSize = Dev->BlockIoMedia.BlockSize; @@ -275,11 +273,7 @@ SynchronousRequest ( Request.IoPrio = 0; Request.Sector = Lba * (BlockSize / 512); - // - // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device. - // We're going to poll the answer, the host should not send an interrupt. - // - *Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT; + VirtioPrepare (&Dev->Ring, &Indices); // // preset a host status for ourselves that we do not accept as success @@ -292,17 +286,11 @@ SynchronousRequest ( // ASSERT (Dev->Ring.QueueSize >= 3); - // - // Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device. - // - FirstAvailIdx = *Dev->Ring.Avail.Idx; - NextAvailIdx = FirstAvailIdx; - // // virtio-blk header in first desc // VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, - VRING_DESC_F_NEXT, FirstAvailIdx, &NextAvailIdx); + VRING_DESC_F_NEXT, &Indices); // // data buffer for read/write in second desc @@ -323,50 +311,20 @@ SynchronousRequest ( // VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize, VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE), - FirstAvailIdx, &NextAvailIdx); + &Indices); } // // host status in last (second or third) desc // VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus, - VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx); + VRING_DESC_F_WRITE, &Indices); // - // virtio-0.9.5, 2.4.1.3 Updating the Index Field + // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). // - MemoryFence(); - *Dev->Ring.Avail.Idx = NextAvailIdx; - - // - // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are - // OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D). - // - MemoryFence(); - if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, 0))) { - return EFI_DEVICE_ERROR; - } - - // - // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device - // Wait until the host processes and acknowledges our 3-part descriptor - // chain. The condition we use for polling is greatly simplified and relies - // on synchronous, the lock-step progress. - // - // Keep slowing down until we reach a poll period of slightly above 1 ms. - // - PollPeriodUsecs = 1; - MemoryFence(); - while (*Dev->Ring.Used.Idx != NextAvailIdx) { - gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay - - if (PollPeriodUsecs < 1024) { - PollPeriodUsecs *= 2; - } - MemoryFence(); - } - - if (HostStatus == VIRTIO_BLK_S_OK) { + if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS && + HostStatus == VIRTIO_BLK_S_OK) { return EFI_SUCCESS; }