mirror of https://github.com/acidanthera/audk.git
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 <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com> git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13844 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
7fcacd6c92
commit
e371e7e545
|
@ -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_
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
**/
|
||||
|
||||
#include <IndustryStandard/Pci22.h>
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
|
||||
#include <Library/VirtioLib.h>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
OvmfPkg/OvmfPkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
BaseLib
|
||||
BaseMemoryLib
|
||||
DebugLib
|
||||
MemoryAllocationLib
|
||||
UefiBootServicesTableLib
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue