X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FVirtioBlkDxe%2FVirtioBlk.c;h=f1c3f87e202700451e7286475cd22217a0167398;hb=fed691a6f9138c3c22d075a9597435be43e660d0;hp=178fe331a42a52e261959903c3054650cd89c1a9;hpb=55c3443a4f9a7bba34f1c60c6121c6b73dd05241;p=mirror_edk2.git
diff --git a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
index 178fe331a4..f1c3f87e20 100644
--- a/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
+++ b/OvmfPkg/VirtioBlkDxe/VirtioBlk.c
@@ -11,6 +11,7 @@
synchronous requests and EFI_BLOCK_IO_PROTOCOL for now.
Copyright (C) 2012, Red Hat, Inc.
+ Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available
under the terms and conditions of the BSD License which accompanies this
@@ -22,163 +23,27 @@
**/
-#include
+#include
#include
#include
#include
#include
#include
+#include
#include "VirtioBlk.h"
-/**
-
- Write a word into Region 0 of the device specified by PciIo.
-
- Region 0 must be an iomem region. This is an internal function for the
- VIRTIO_CFG_WRITE() macro below.
-
- @param[in] PciIo Target PCI device.
-
- @param[in] FieldOffset Destination offset.
-
- @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.
-
- @param[in] Value Little endian value to write, converted to UINT64.
- The least significant FieldSize bytes will be used.
-
-
- @return Status code returned by PciIo->Io.Write().
-
-**/
-STATIC
-EFIAPI
-EFI_STATUS
-VirtioWrite (
- IN EFI_PCI_IO_PROTOCOL *PciIo,
- IN UINTN FieldOffset,
- IN UINTN FieldSize,
- IN UINT64 Value
- )
-{
- UINTN Count;
- EFI_PCI_IO_PROTOCOL_WIDTH Width;
-
- Count = 1;
- switch (FieldSize) {
- case 1:
- Width = EfiPciIoWidthUint8;
- break;
-
- case 2:
- Width = EfiPciIoWidthUint16;
- break;
-
- case 8:
- Count = 2;
- // fall through
-
- case 4:
- Width = EfiPciIoWidthUint32;
- break;
-
- default:
- ASSERT (FALSE);
- }
-
- return PciIo->Io.Write (
- PciIo,
- Width,
- PCI_BAR_IDX0,
- FieldOffset,
- Count,
- &Value
- );
-}
-
-
-/**
-
- Read a word from Region 0 of the device specified by PciIo.
-
- Region 0 must be an iomem region. This is an internal function for the
- VIRTIO_CFG_READ() macro below.
-
- @param[in] PciIo Source PCI device.
-
- @param[in] FieldOffset Source offset.
-
- @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.
-
- @param[in] BufferSize Number of bytes available in the target buffer. Must
- equal FieldSize.
-
- @param[out] Buffer Target buffer.
-
-
- @return Status code returned by PciIo->Io.Read().
-
-**/
-STATIC
-EFIAPI
-EFI_STATUS
-VirtioRead (
- IN EFI_PCI_IO_PROTOCOL *PciIo,
- IN UINTN FieldOffset,
- IN UINTN FieldSize,
- IN UINTN BufferSize,
- OUT VOID *Buffer
- )
-{
- UINTN Count;
- EFI_PCI_IO_PROTOCOL_WIDTH Width;
-
- ASSERT (FieldSize == BufferSize);
-
- Count = 1;
- switch (FieldSize) {
- case 1:
- Width = EfiPciIoWidthUint8;
- break;
-
- case 2:
- Width = EfiPciIoWidthUint16;
- break;
-
- case 8:
- Count = 2;
- // fall through
-
- case 4:
- Width = EfiPciIoWidthUint32;
- break;
-
- default:
- ASSERT (FALSE);
- }
-
- return PciIo->Io.Read (
- PciIo,
- Width,
- PCI_BAR_IDX0,
- FieldOffset,
- Count,
- Buffer
- );
-}
-
-
/**
Convenience macros to read and write region 0 IO space elements of the
- virtio-blk PCI device, for configuration purposes.
+ virtio-blk device, for configuration purposes.
The following macros make it possible to specify only the "core parameters"
for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()
returns, the transaction will have been completed.
- @param[in] Dev Pointer to the VBLK_DEV structure whose PCI IO space
- we're accessing. Dev->PciIo must be valid.
+ @param[in] Dev Pointer to the VBLK_DEV structure whose VirtIo space
+ we're accessing. Dev->VirtIo must be valid.
@param[in] Field A field name from VBLK_HDR, identifying the virtio-blk
configuration item to access.
@@ -191,23 +56,24 @@ VirtioRead (
one of UINT8, UINT16, UINT32, UINT64.
- @return Status code returned by VirtioWrite() / VirtioRead().
+ @return Status code returned by Virtio->WriteDevice() /
+ Virtio->ReadDevice().
**/
-#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \
- (Dev)->PciIo, \
- OFFSET_OF_VHDR (Field), \
- SIZE_OF_VHDR (Field), \
- (Value) \
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \
+ (Dev)->VirtIo, \
+ OFFSET_OF_VBLK (Field), \
+ SIZE_OF_VBLK (Field), \
+ (Value) \
))
-#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \
- (Dev)->PciIo, \
- OFFSET_OF_VHDR (Field), \
- SIZE_OF_VHDR (Field), \
- sizeof *(Pointer), \
- (Pointer) \
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \
+ (Dev)->VirtIo, \
+ OFFSET_OF_VBLK (Field), \
+ SIZE_OF_VBLK (Field), \
+ sizeof *(Pointer), \
+ (Pointer) \
))
@@ -313,66 +179,6 @@ VerifyReadWriteRequest (
}
-/**
-
- Append a contiguous buffer for transmission / reception via the virtio ring.
-
- This function implements the following sections from virtio-0.9.5:
- - 2.4.1.1 Placing Buffers into the Descriptor Table
- - 2.4.1.2 Updating the Available Ring
-
- Free space is taken as granted, since this driver supports only synchronous
- requests and host side status is processed in lock-step with request
- submission. VirtioBlkInit() verifies the ring size in advance.
-
- @param[in out] Ring The virtio ring to append the buffer to, as a
- descriptor.
-
- @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the
- transmit / receive buffer
-
- @param [in] BufferSize Number of bytes to transmit or receive.
-
- @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller
- computes this mask dependent on further buffers
- to append and transfer direction.
- VRING_DESC_F_INDIRECT is unsupported. The
- VRING_DESC.Next field is always set, but the
- host only interprets it dependent on
- VRING_DESC_F_NEXT.
-
- @param [in] HeadIdx The index identifying the head buffer (first
- buffer appended) belonging to this same
- request.
-
- @param [in out] NextAvailIdx On input, the index identifying the next
- descriptor available to carry the buffer. On
- output, incremented by one, modulo 2^16.
-
-**/
-
-STATIC
-VOID
-EFIAPI
-AppendDesc (
- IN OUT VRING *Ring,
- IN UINTN BufferPhysAddr,
- IN UINT32 BufferSize,
- IN UINT16 Flags,
- IN UINT16 HeadIdx,
- IN OUT UINT16 *NextAvailIdx
- )
-{
- volatile VRING_DESC *Desc;
-
- Desc = &Ring->Desc[*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;
-}
/**
@@ -423,7 +229,7 @@ AppendDesc (
@retval EFI_SUCCESS Transfer complete.
- @retval EFI_DEVICE_ERROR Failed to notify host side via PCI write, or
+ @retval EFI_DEVICE_ERROR Failed to notify host side via VirtIo write, or
unable to parse host response, or host response
is not VIRTIO_BLK_S_OK.
@@ -443,9 +249,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;
@@ -468,13 +272,9 @@ SynchronousRequest (
(BufferSize == 0 ? VIRTIO_BLK_T_FLUSH : VIRTIO_BLK_T_OUT) :
VIRTIO_BLK_T_IN;
Request.IoPrio = 0;
- Request.Sector = Lba * (BlockSize / 512);
+ Request.Sector = MultU64x32(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
@@ -487,17 +287,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
//
- AppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, VRING_DESC_F_NEXT,
- FirstAvailIdx, &NextAvailIdx);
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
+ VRING_DESC_F_NEXT, &Indices);
//
// data buffer for read/write in second desc
@@ -516,52 +310,23 @@ SynchronousRequest (
//
// VRING_DESC_F_WRITE is interpreted from the host's point of view.
//
- AppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
+ 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
//
- AppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,
- VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx);
-
- //
- // virtio-0.9.5, 2.4.1.3 Updating the Index Field
- //
- 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;
- }
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,
+ VRING_DESC_F_WRITE, &Indices);
//
- // 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.
+ // virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).
//
- // 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->VirtIo, 0, &Dev->Ring, &Indices,
+ NULL) == EFI_SUCCESS &&
+ HostStatus == VIRTIO_BLK_S_OK) {
return EFI_SUCCESS;
}
@@ -736,11 +501,6 @@ VirtioBlkFlushBlocks (
underlying device
- 9 Driver Binding Protocol -- for exporting ourselves
- Specs relevant in the specific sense:
- - UEFI Spec 2.3.1 + Errata C, 13.4 EFI PCI I/O Protocol
- - Driver Writer's Guide for UEFI 2.3.1 v1.01, 18 PCI Driver Design
- Guidelines, 18.3 PCI drivers.
-
@param[in] This The EFI_DRIVER_BINDING_PROTOCOL object
incorporating this driver (independently of
any device).
@@ -752,11 +512,11 @@ VirtioBlkFlushBlocks (
@retval EFI_SUCCESS The driver supports the device being probed.
- @retval EFI_UNSUPPORTED Based on virtio-blk PCI discovery, we do not support
+ @retval EFI_UNSUPPORTED Based on virtio-blk discovery, we do not support
the device.
@return Error codes from the OpenProtocol() boot service or
- the PciIo protocol.
+ the VirtIo protocol.
**/
@@ -769,191 +529,49 @@ VirtioBlkDriverBindingSupported (
)
{
EFI_STATUS Status;
- EFI_PCI_IO_PROTOCOL *PciIo;
- PCI_TYPE00 Pci;
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;
//
- // Attempt to open the device with the PciIo set of interfaces. On success,
- // the protocol is "instantiated" for the PCI device. Covers duplicate open
- // attempts (EFI_ALREADY_STARTED).
+ // Attempt to open the device with the VirtIo set of interfaces. On success,
+ // the protocol is "instantiated" for the VirtIo device. Covers duplicate
+ // open attempts (EFI_ALREADY_STARTED).
//
Status = gBS->OpenProtocol (
DeviceHandle, // candidate device
- &gEfiPciIoProtocolGuid, // for generic PCI access
- (VOID **)&PciIo, // handle to instantiate
+ &gVirtioDeviceProtocolGuid, // for generic VirtIo access
+ (VOID **)&VirtIo, // handle to instantiate
This->DriverBindingHandle, // requestor driver identity
DeviceHandle, // ControllerHandle, according to
// the UEFI Driver Model
- EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
// the device; to be released
);
if (EFI_ERROR (Status)) {
return Status;
}
- //
- // Read entire PCI configuration header for more extensive check ahead.
- //
- Status = PciIo->Pci.Read (
- PciIo, // (protocol, device)
- // handle
- EfiPciIoWidthUint32, // access width & copy
- // mode
- 0, // Offset
- sizeof Pci / sizeof (UINT32), // Count
- &Pci // target buffer
- );
-
- if (Status == EFI_SUCCESS) {
- //
- // virtio-0.9.5, 2.1 PCI Discovery
- //
- Status = (Pci.Hdr.VendorId == 0x1AF4 &&
- Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F &&
- Pci.Hdr.RevisionID == 0x00 &&
- Pci.Device.SubsystemID == 0x02) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+ if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_BLOCK_DEVICE) {
+ Status = EFI_UNSUPPORTED;
}
//
- // We needed PCI IO access only transitorily, to see whether we support the
+ // We needed VirtIo access only transitorily, to see whether we support the
// device or not.
//
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
This->DriverBindingHandle, DeviceHandle);
return Status;
}
-/**
-
- Configure a virtio ring.
-
- This function sets up internal storage (the guest-host communication area)
- and lays out several "navigation" (ie. no-ownership) pointers to parts of
- that storage.
-
- Relevant sections from the virtio-0.9.5 spec:
- - 1.1 Virtqueues,
- - 2.3 Virtqueue Configuration.
-
- @param[in] The number of descriptors to allocate for the
- virtio ring, as requested by the host.
-
- @param[out] Ring The virtio ring to set up.
-
- @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous
- pages for the requested QueueSize. Fields of
- Ring have indeterminate value.
-
- @retval EFI_SUCCESS Allocation and setup successful. Ring->Base
- (and nothing else) is responsible for
- deallocation.
-
-**/
-
-STATIC
-EFI_STATUS
-EFIAPI
-VirtioRingInit (
- IN UINT16 QueueSize,
- OUT VRING *Ring
- )
-{
- UINTN RingSize;
- volatile UINT8 *RingPagesPtr;
-
- RingSize = ALIGN_VALUE (
- sizeof *Ring->Desc * QueueSize +
- sizeof *Ring->Avail.Flags +
- sizeof *Ring->Avail.Idx +
- sizeof *Ring->Avail.Ring * QueueSize +
- sizeof *Ring->Avail.UsedEvent,
- EFI_PAGE_SIZE);
-
- RingSize += ALIGN_VALUE (
- sizeof *Ring->Used.Flags +
- sizeof *Ring->Used.Idx +
- sizeof *Ring->Used.UsedElem * QueueSize +
- sizeof *Ring->Used.AvailEvent,
- EFI_PAGE_SIZE);
-
- Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);
- Ring->Base = AllocatePages (Ring->NumPages);
- if (Ring->Base == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- SetMem (Ring->Base, RingSize, 0x00);
- RingPagesPtr = Ring->Base;
-
- Ring->Desc = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Desc * QueueSize;
-
- Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Avail.Flags;
-
- Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Avail.Idx;
-
- Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;
-
- Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Avail.UsedEvent;
-
- RingPagesPtr = (volatile UINT8 *) Ring->Base +
- ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,
- EFI_PAGE_SIZE);
-
- Ring->Used.Flags = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Used.Flags;
-
- Ring->Used.Idx = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Used.Idx;
-
- Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;
-
- Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;
- RingPagesPtr += sizeof *Ring->Used.AvailEvent;
-
- Ring->QueueSize = QueueSize;
- return EFI_SUCCESS;
-}
-
-
-/**
-
- Tear down the internal resources of a configured virtio ring.
-
- The caller is responsible to stop the host from using this ring before
- invoking this function: the VSTAT_DRIVER_OK bit must be clear in
- VhdrDeviceStatus.
-
- @param[out] Ring The virtio ring to clean up.
-
-**/
-
-
-STATIC
-VOID
-EFIAPI
-VirtioRingUninit (
- IN OUT VRING *Ring
- )
-{
- FreePages (Ring->Base, Ring->NumPages);
- SetMem (Ring, sizeof *Ring, 0x00);
-}
-
-
/**
Set up all BlockIo and virtio-blk aspects of this driver for the specified
device.
@param[in out] Dev The driver instance to configure. The caller is
- responsible for Dev->PciIo's validity (ie. working IO
- access to the underlying virtio-blk PCI device).
+ responsible for Dev->VirtIo's validity (ie. working IO
+ access to the underlying virtio-blk device).
@retval EFI_SUCCESS Setup complete.
@@ -975,28 +593,43 @@ VirtioBlkInit (
UINT8 NextDevStat;
EFI_STATUS Status;
- UINT32 Features;
+ UINT64 Features;
UINT64 NumSectors;
UINT32 BlockSize;
+ UINT8 PhysicalBlockExp;
+ UINT8 AlignmentOffset;
+ UINT32 OptIoSize;
UINT16 QueueSize;
+ PhysicalBlockExp = 0;
+ AlignmentOffset = 0;
+ OptIoSize = 0;
+
//
// Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
//
NextDevStat = 0; // step 1 -- reset device
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
if (EFI_ERROR (Status)) {
goto Failed;
}
NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
if (EFI_ERROR (Status)) {
goto Failed;
}
NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // Set Page Size - MMIO VirtIo Specific
+ //
+ Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -1004,11 +637,12 @@ VirtioBlkInit (
//
// step 4a -- retrieve and validate features
//
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
if (EFI_ERROR (Status)) {
goto Failed;
}
- Status = VIRTIO_CFG_READ (Dev, VhdrCapacity, &NumSectors);
+
+ Status = VIRTIO_CFG_READ (Dev, Capacity, &NumSectors);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -1018,12 +652,12 @@ VirtioBlkInit (
}
if (Features & VIRTIO_BLK_F_BLK_SIZE) {
- Status = VIRTIO_CFG_READ (Dev, VhdrBlkSize, &BlockSize);
+ Status = VIRTIO_CFG_READ (Dev, BlkSize, &BlockSize);
if (EFI_ERROR (Status)) {
goto Failed;
}
if (BlockSize == 0 || BlockSize % 512 != 0 ||
- NumSectors % (BlockSize / 512) != 0) {
+ ModU64x32 (NumSectors, BlockSize / 512) != 0) {
//
// We can only handle a logical block consisting of whole sectors,
// and only a disk composed of whole logical blocks.
@@ -1036,14 +670,50 @@ VirtioBlkInit (
BlockSize = 512;
}
+ if (Features & VIRTIO_BLK_F_TOPOLOGY) {
+ Status = VIRTIO_CFG_READ (Dev, Topology.PhysicalBlockExp,
+ &PhysicalBlockExp);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (PhysicalBlockExp >= 32) {
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, Topology.AlignmentOffset, &AlignmentOffset);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, Topology.OptIoSize, &OptIoSize);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ }
+
+ Features &= VIRTIO_BLK_F_BLK_SIZE | VIRTIO_BLK_F_TOPOLOGY | VIRTIO_BLK_F_RO |
+ VIRTIO_BLK_F_FLUSH | VIRTIO_F_VERSION_1;
+
+ //
+ // In virtio-1.0, feature negotiation is expected to complete before queue
+ // discovery, and the device can also reject the selected set of features.
+ //
+ if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ }
+
//
// step 4b -- allocate virtqueue
//
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect, 0);
+ Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, 0);
if (EFI_ERROR (Status)) {
goto Failed;
}
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize);
+ Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -1058,39 +728,51 @@ VirtioBlkInit (
}
//
- // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything
- // fails from here on, we must release the ring resources.
+ // Additional steps for MMIO: align the queue appropriately, and set the
+ // size. If anything fails from here on, we must release the ring resources.
//
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueAddress,
- (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);
+ Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
//
- // step 5 -- Report understood features. There are no virtio-blk specific
- // features to negotiate in virtio-0.9.5, plus we do not want any of the
- // device-independent (known or unknown) VIRTIO_F_* capabilities (see
- // Appendix B).
+ // step 4c -- Report GPFN (guest-physical frame number) of queue.
//
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits, 0);
+ Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, &Dev->Ring);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
+
+ //
+ // step 5 -- Report understood features.
+ //
+ if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
+ Features &= ~(UINT64)VIRTIO_F_VERSION_1;
+ Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+ }
+
//
// step 6 -- initialization complete
//
NextDevStat |= VSTAT_DRIVER_OK;
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
//
- // Populate the exported interface's attributes; see UEFI spec v2.3.1 +
- // Errata C, 12.8 EFI Block I/O Protocol. We stick to the lowest possible
- // EFI_BLOCK_IO_PROTOCOL revision for now.
+ // Populate the exported interface's attributes; see UEFI spec v2.4, 12.9 EFI
+ // Block I/O Protocol.
//
Dev->BlockIo.Revision = 0;
Dev->BlockIo.Media = &Dev->BlockIoMedia;
@@ -1102,11 +784,30 @@ VirtioBlkInit (
Dev->BlockIoMedia.RemovableMedia = FALSE;
Dev->BlockIoMedia.MediaPresent = TRUE;
Dev->BlockIoMedia.LogicalPartition = FALSE;
- Dev->BlockIoMedia.ReadOnly = !!(Features & VIRTIO_BLK_F_RO);
- Dev->BlockIoMedia.WriteCaching = !!(Features & VIRTIO_BLK_F_FLUSH);
+ Dev->BlockIoMedia.ReadOnly = (BOOLEAN) ((Features & VIRTIO_BLK_F_RO) != 0);
+ Dev->BlockIoMedia.WriteCaching = (BOOLEAN) ((Features & VIRTIO_BLK_F_FLUSH) != 0);
Dev->BlockIoMedia.BlockSize = BlockSize;
Dev->BlockIoMedia.IoAlign = 0;
- Dev->BlockIoMedia.LastBlock = NumSectors / (BlockSize / 512) - 1;
+ Dev->BlockIoMedia.LastBlock = DivU64x32 (NumSectors,
+ BlockSize / 512) - 1;
+
+ DEBUG ((DEBUG_INFO, "%a: LbaSize=0x%x[B] NumBlocks=0x%Lx[Lba]\n",
+ __FUNCTION__, Dev->BlockIoMedia.BlockSize,
+ Dev->BlockIoMedia.LastBlock + 1));
+
+ if (Features & VIRTIO_BLK_F_TOPOLOGY) {
+ Dev->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;
+
+ Dev->BlockIoMedia.LowestAlignedLba = AlignmentOffset;
+ Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock = 1u << PhysicalBlockExp;
+ Dev->BlockIoMedia.OptimalTransferLengthGranularity = OptIoSize;
+
+ DEBUG ((DEBUG_INFO, "%a: FirstAligned=0x%Lx[Lba] PhysBlkSize=0x%x[Lba]\n",
+ __FUNCTION__, Dev->BlockIoMedia.LowestAlignedLba,
+ Dev->BlockIoMedia.LogicalBlocksPerPhysicalBlock));
+ DEBUG ((DEBUG_INFO, "%a: OptimalTransferLengthGranularity=0x%x[Lba]\n",
+ __FUNCTION__, Dev->BlockIoMedia.OptimalTransferLengthGranularity));
+ }
return EFI_SUCCESS;
ReleaseQueue:
@@ -1115,10 +816,10 @@ ReleaseQueue:
Failed:
//
// Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
- // Status. PCI IO access failure here should not mask the original error.
+ // Status. VirtIo access failure here should not mask the original error.
//
NextDevStat |= VSTAT_FAILED;
- VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
return Status; // reached only via Failed above
}
@@ -1145,7 +846,7 @@ VirtioBlkUninit (
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
// the old comms area.
//
- VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, 0);
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
VirtioRingUninit (&Dev->Ring);
@@ -1154,6 +855,37 @@ VirtioBlkUninit (
}
+/**
+
+ Event notification function enqueued by ExitBootServices().
+
+ @param[in] Event Event whose notification function is being invoked.
+
+ @param[in] Context Pointer to the VBLK_DEV structure.
+
+**/
+
+STATIC
+VOID
+EFIAPI
+VirtioBlkExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ VBLK_DEV *Dev;
+
+ //
+ // Reset the device. This causes the hypervisor to forget about the virtio
+ // ring.
+ //
+ // We allocated said ring in EfiBootServicesData type memory, and code
+ // executing after ExitBootServices() is permitted to overwrite it.
+ //
+ Dev = Context;
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
+}
+
/**
After we've pronounced support for a specific device in
@@ -1172,13 +904,13 @@ VirtioBlkUninit (
@retval EFI_SUCCESS Driver instance has been created and
- initialized for the virtio-blk PCI device, it
+ initialized for the virtio-blk device, it
is now accessibla via EFI_BLOCK_IO_PROTOCOL.
@retval EFI_OUT_OF_RESOURCES Memory allocation failed.
@return Error codes from the OpenProtocol() boot
- service, the PciIo protocol, VirtioBlkInit(),
+ service, the VirtIo protocol, VirtioBlkInit(),
or the InstallProtocolInterface() boot service.
**/
@@ -1199,43 +931,25 @@ VirtioBlkDriverBindingStart (
return EFI_OUT_OF_RESOURCES;
}
- Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
- (VOID **)&Dev->PciIo, This->DriverBindingHandle,
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
+ (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
if (EFI_ERROR (Status)) {
goto FreeVirtioBlk;
}
//
- // We must retain and ultimately restore the original PCI attributes of the
- // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /
- // 18.3.2 Start() and Stop().
- //
- // The third parameter ("Attributes", input) is ignored by the Get operation.
- // The fourth parameter ("Result", output) is ignored by the Enable and Set
- // operations.
+ // VirtIo access granted, configure virtio-blk device.
//
- // For virtio-blk we only need IO space access.
- //
- Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet,
- 0, &Dev->OriginalPciAttributes);
- if (EFI_ERROR (Status)) {
- goto ClosePciIo;
- }
-
- Status = Dev->PciIo->Attributes (Dev->PciIo,
- EfiPciIoAttributeOperationEnable,
- EFI_PCI_IO_ATTRIBUTE_IO, NULL);
+ Status = VirtioBlkInit (Dev);
if (EFI_ERROR (Status)) {
- goto ClosePciIo;
+ goto CloseVirtIo;
}
- //
- // PCI IO access granted, configure virtio-blk device.
- //
- Status = VirtioBlkInit (Dev);
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+ &VirtioBlkExitBoot, Dev, &Dev->ExitBoot);
if (EFI_ERROR (Status)) {
- goto RestorePciAttributes;
+ goto UninitDev;
}
//
@@ -1246,20 +960,19 @@ VirtioBlkDriverBindingStart (
&gEfiBlockIoProtocolGuid, EFI_NATIVE_INTERFACE,
&Dev->BlockIo);
if (EFI_ERROR (Status)) {
- goto UninitDev;
+ goto CloseExitBoot;
}
return EFI_SUCCESS;
+CloseExitBoot:
+ gBS->CloseEvent (Dev->ExitBoot);
+
UninitDev:
VirtioBlkUninit (Dev);
-RestorePciAttributes:
- Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
- Dev->OriginalPciAttributes, NULL);
-
-ClosePciIo:
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+CloseVirtIo:
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
This->DriverBindingHandle, DeviceHandle);
FreeVirtioBlk:
@@ -1301,25 +1014,38 @@ VirtioBlkDriverBindingStop (
IN EFI_HANDLE *ChildHandleBuffer
)
{
- VBLK_DEV *Dev;
- EFI_STATUS Status;
+ EFI_STATUS Status;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ VBLK_DEV *Dev;
- Dev = VIRTIO_BLK_FROM_BLOCK_IO (This);
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gEfiBlockIoProtocolGuid, // retrieve the BlockIo iface
+ (VOID **)&BlockIo, // target pointer
+ This->DriverBindingHandle, // requestor driver identity
+ DeviceHandle, // requesting lookup for dev.
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Dev = VIRTIO_BLK_FROM_BLOCK_IO (BlockIo);
//
- // If DriverBindingStop() is called with the driver instance still in use,
- // or any of the parameters are invalid, we've caught a bug.
+ // Handle Stop() requests for in-use driver instances gracefully.
//
Status = gBS->UninstallProtocolInterface (DeviceHandle,
&gEfiBlockIoProtocolGuid, &Dev->BlockIo);
- ASSERT (Status == EFI_SUCCESS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
- VirtioBlkUninit (Dev);
+ gBS->CloseEvent (Dev->ExitBoot);
- Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
- Dev->OriginalPciAttributes, NULL);
+ VirtioBlkUninit (Dev);
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
This->DriverBindingHandle, DeviceHandle);
FreePool (Dev);
@@ -1356,13 +1082,13 @@ STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
// for unambiguous identification.
//
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
{ "eng;en", L"Virtio Block Driver" },
{ NULL, NULL }
};
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
EFI_COMPONENT_NAME_PROTOCOL gComponentName;
EFI_STATUS
@@ -1395,14 +1121,14 @@ VirtioBlkGetDeviceName (
return EFI_UNSUPPORTED;
}
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
&VirtioBlkGetDriverName,
&VirtioBlkGetDeviceName,
"eng" // SupportedLanguages, ISO 639-2 language codes
};
-STATIC GLOBAL_REMOVE_IF_UNREFERENCED
+STATIC
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioBlkGetDriverName,
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioBlkGetDeviceName,