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.
|
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
|
request submission. It is the calling driver's responsibility to verify the
|
||||||
ring size in advance.
|
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
|
@param[in out] Ring The virtio ring to append the buffer to, as a
|
||||||
descriptor.
|
descriptor.
|
||||||
|
|
||||||
|
@ -164,6 +197,8 @@ VirtioRingUninit (
|
||||||
host only interprets it dependent on
|
host only interprets it dependent on
|
||||||
VRING_DESC_F_NEXT.
|
VRING_DESC_F_NEXT.
|
||||||
|
|
||||||
|
In *Indices:
|
||||||
|
|
||||||
@param [in] HeadIdx The index identifying the head buffer (first
|
@param [in] HeadIdx The index identifying the head buffer (first
|
||||||
buffer appended) belonging to this same
|
buffer appended) belonging to this same
|
||||||
request.
|
request.
|
||||||
|
@ -176,12 +211,41 @@ VirtioRingUninit (
|
||||||
VOID
|
VOID
|
||||||
EFIAPI
|
EFIAPI
|
||||||
VirtioAppendDesc (
|
VirtioAppendDesc (
|
||||||
IN OUT VRING *Ring,
|
IN OUT VRING *Ring,
|
||||||
IN UINTN BufferPhysAddr,
|
IN UINTN BufferPhysAddr,
|
||||||
IN UINT32 BufferSize,
|
IN UINT32 BufferSize,
|
||||||
IN UINT16 Flags,
|
IN UINT16 Flags,
|
||||||
IN UINT16 HeadIdx,
|
IN OUT DESC_INDICES *Indices
|
||||||
IN OUT UINT16 *NextAvailIdx
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
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_
|
#endif // _VIRTIO_LIB_H_
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#include <IndustryStandard/Pci22.h>
|
#include <IndustryStandard/Pci22.h>
|
||||||
|
#include <Library/BaseLib.h>
|
||||||
#include <Library/BaseMemoryLib.h>
|
#include <Library/BaseMemoryLib.h>
|
||||||
#include <Library/DebugLib.h>
|
#include <Library/DebugLib.h>
|
||||||
#include <Library/MemoryAllocationLib.h>
|
#include <Library/MemoryAllocationLib.h>
|
||||||
|
#include <Library/UefiBootServicesTableLib.h>
|
||||||
|
|
||||||
#include <Library/VirtioLib.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.
|
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
|
request submission. It is the calling driver's responsibility to verify the
|
||||||
ring size in advance.
|
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
|
@param[in out] Ring The virtio ring to append the buffer to, as a
|
||||||
descriptor.
|
descriptor.
|
||||||
|
|
||||||
|
@ -303,6 +341,8 @@ VirtioRingUninit (
|
||||||
host only interprets it dependent on
|
host only interprets it dependent on
|
||||||
VRING_DESC_F_NEXT.
|
VRING_DESC_F_NEXT.
|
||||||
|
|
||||||
|
In *Indices:
|
||||||
|
|
||||||
@param [in] HeadIdx The index identifying the head buffer (first
|
@param [in] HeadIdx The index identifying the head buffer (first
|
||||||
buffer appended) belonging to this same
|
buffer appended) belonging to this same
|
||||||
request.
|
request.
|
||||||
|
@ -315,21 +355,96 @@ VirtioRingUninit (
|
||||||
VOID
|
VOID
|
||||||
EFIAPI
|
EFIAPI
|
||||||
VirtioAppendDesc (
|
VirtioAppendDesc (
|
||||||
IN OUT VRING *Ring,
|
IN OUT VRING *Ring,
|
||||||
IN UINTN BufferPhysAddr,
|
IN UINTN BufferPhysAddr,
|
||||||
IN UINT32 BufferSize,
|
IN UINT32 BufferSize,
|
||||||
IN UINT16 Flags,
|
IN UINT16 Flags,
|
||||||
IN UINT16 HeadIdx,
|
IN OUT DESC_INDICES *Indices
|
||||||
IN OUT UINT16 *NextAvailIdx
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
volatile VRING_DESC *Desc;
|
volatile VRING_DESC *Desc;
|
||||||
|
|
||||||
Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];
|
Desc = &Ring->Desc[Indices->NextAvailIdx % Ring->QueueSize];
|
||||||
Desc->Addr = BufferPhysAddr;
|
Desc->Addr = BufferPhysAddr;
|
||||||
Desc->Len = BufferSize;
|
Desc->Len = BufferSize;
|
||||||
Desc->Flags = Flags;
|
Desc->Flags = Flags;
|
||||||
Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =
|
Ring->Avail.Ring[Indices->NextAvailIdx++ % Ring->QueueSize] =
|
||||||
HeadIdx % Ring->QueueSize;
|
Indices->HeadIdx % Ring->QueueSize;
|
||||||
Desc->Next = *NextAvailIdx % 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
|
OvmfPkg/OvmfPkg.dec
|
||||||
|
|
||||||
[LibraryClasses]
|
[LibraryClasses]
|
||||||
|
BaseLib
|
||||||
BaseMemoryLib
|
BaseMemoryLib
|
||||||
DebugLib
|
DebugLib
|
||||||
MemoryAllocationLib
|
MemoryAllocationLib
|
||||||
|
UefiBootServicesTableLib
|
||||||
|
|
|
@ -248,9 +248,7 @@ SynchronousRequest (
|
||||||
UINT32 BlockSize;
|
UINT32 BlockSize;
|
||||||
volatile VIRTIO_BLK_REQ Request;
|
volatile VIRTIO_BLK_REQ Request;
|
||||||
volatile UINT8 HostStatus;
|
volatile UINT8 HostStatus;
|
||||||
UINT16 FirstAvailIdx;
|
DESC_INDICES Indices;
|
||||||
UINT16 NextAvailIdx;
|
|
||||||
UINTN PollPeriodUsecs;
|
|
||||||
|
|
||||||
BlockSize = Dev->BlockIoMedia.BlockSize;
|
BlockSize = Dev->BlockIoMedia.BlockSize;
|
||||||
|
|
||||||
|
@ -275,11 +273,7 @@ SynchronousRequest (
|
||||||
Request.IoPrio = 0;
|
Request.IoPrio = 0;
|
||||||
Request.Sector = Lba * (BlockSize / 512);
|
Request.Sector = Lba * (BlockSize / 512);
|
||||||
|
|
||||||
//
|
VirtioPrepare (&Dev->Ring, &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.
|
|
||||||
//
|
|
||||||
*Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// preset a host status for ourselves that we do not accept as success
|
// preset a host status for ourselves that we do not accept as success
|
||||||
|
@ -292,17 +286,11 @@ SynchronousRequest (
|
||||||
//
|
//
|
||||||
ASSERT (Dev->Ring.QueueSize >= 3);
|
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
|
// virtio-blk header in first desc
|
||||||
//
|
//
|
||||||
VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
|
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
|
// data buffer for read/write in second desc
|
||||||
|
@ -323,50 +311,20 @@ SynchronousRequest (
|
||||||
//
|
//
|
||||||
VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
|
VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
|
||||||
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
|
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
|
||||||
FirstAvailIdx, &NextAvailIdx);
|
&Indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// host status in last (second or third) desc
|
// host status in last (second or third) desc
|
||||||
//
|
//
|
||||||
VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,
|
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();
|
if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&
|
||||||
*Dev->Ring.Avail.Idx = NextAvailIdx;
|
HostStatus == VIRTIO_BLK_S_OK) {
|
||||||
|
|
||||||
//
|
|
||||||
// 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) {
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue