OvmfPkg: Make the VirtIo devices use the new VIRTIO_DEVICE_PROTOCOL

This change replaces the accesses to the PCI bus from the Block, Scsi and Net drivers by
the use of the new VIRTIO_DEVICE_PROTOCOL protocol that abstracts the transport layer.
It means these drivers can be used on PCI and MMIO transport layer.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>

v5:
- VirtioFlush(): update comment block in VirtioLib.[hc]; error code is
  propagated from VirtIo->SetQueueNotify().
- VirtioBlkInit(): jump to Failed label if SetPageSize() fails
- VirtioBlkInit(): fixup comment, and add error handling, near
  SetQueueNum() call
- VirtioBlkDriverBindingStart(): remove redundant (always false) check for
  a subsystem device ID different from VIRTIO_SUBSYSTEM_BLOCK_DEVICE;
  VirtioBlkDriverBindingSupported() handles it already
- VirtioNetGetFeatures(): update stale comment block
- VirtioNetGetFeatures(): retrieve MAC address byte for byte (open-coded
  loop)
- VirtioNetDriverBindingStart(): remove redundant (always false) check for
  a subsystem device ID different from VIRTIO_SUBSYSTEM_NETWORK_CARD;
  VirtioNetDriverBindingSupported() handles it already
- VirtioNetInitRing(): call SetQueueNum() and SetQueueAlign() for proper
  MMIO operation
- VirtioNetInitialize(): fix destination error label for when
  SetPageSize() fails
- VirtioScsi.c: fix comment block of VIRTIO_CFG_WRITE()/VIRTIO_CFG_READ()
- VirtioScsiInit(): fix destination error label for when SetPageSize()
  fails
- VirtioScsiInit(): call SetQueueNum() and SetQueueAlign() for proper MMIO
  operation

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://svn.code.sf.net/p/edk2/code/trunk/edk2@14966 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Olivier Martin
2013-12-11 16:58:22 +00:00
committed by jljusten
parent 6fb4e772a0
commit 56f65ed838
27 changed files with 388 additions and 532 deletions

View File

@@ -38,7 +38,6 @@
**/
#include <IndustryStandard/Pci.h>
#include <IndustryStandard/VirtioScsi.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
@@ -51,15 +50,14 @@
/**
Convenience macros to read and write region 0 IO space elements of the
virtio-scsi PCI device, for configuration purposes.
Convenience macros to read and write configuration elements of the
virtio-scsi VirtIo device.
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 VSCSI_DEV structure whose PCI IO space
we're accessing. Dev->PciIo must be valid.
@param[in] Dev Pointer to the VSCSI_DEV structure.
@param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
configuration item to access.
@@ -72,19 +70,19 @@
one of UINT8, UINT16, UINT32, UINT64.
@return Status codes returned by VirtioWrite() / VirtioRead().
@return Status codes returned by VirtioWriteDevice() / VirtioReadDevice().
**/
#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \
(Dev)->PciIo, \
#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWriteDevice ( \
(Dev)->VirtIo, \
OFFSET_OF_VSCSI (Field), \
SIZE_OF_VSCSI (Field), \
(Value) \
))
#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \
(Dev)->PciIo, \
#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioReadDevice ( \
(Dev)->VirtIo, \
OFFSET_OF_VSCSI (Field), \
SIZE_OF_VSCSI (Field), \
sizeof *(Pointer), \
@@ -471,7 +469,7 @@ VirtioScsiPassThru (
// EFI_NOT_READY would save us the effort, but it would also suggest that the
// caller retry.
//
if (VirtioFlush (Dev->PciIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,
if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,
&Indices) != EFI_SUCCESS) {
Packet->InTransferLength = 0;
Packet->OutTransferLength = 0;
@@ -718,19 +716,27 @@ VirtioScsiInit (
// 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;
}
@@ -738,13 +744,13 @@ VirtioScsiInit (
//
// 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;
}
Dev->InOutSupported = !!(Features & VIRTIO_SCSI_F_INOUT);
Status = VIRTIO_CFG_READ (Dev, VhdrMaxChannel, &MaxChannel);
Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -756,7 +762,7 @@ VirtioScsiInit (
goto Failed;
}
Status = VIRTIO_CFG_READ (Dev, VhdrNumQueues, &NumQueues);
Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -765,7 +771,7 @@ VirtioScsiInit (
goto Failed;
}
Status = VIRTIO_CFG_READ (Dev, VhdrMaxTarget, &Dev->MaxTarget);
Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -773,7 +779,7 @@ VirtioScsiInit (
Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);
}
Status = VIRTIO_CFG_READ (Dev, VhdrMaxLun, &Dev->MaxLun);
Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -781,7 +787,7 @@ VirtioScsiInit (
Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);
}
Status = VIRTIO_CFG_READ (Dev, VhdrMaxSectors, &Dev->MaxSectors);
Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);
if (EFI_ERROR (Status)) {
goto Failed;
}
@@ -797,12 +803,11 @@ VirtioScsiInit (
//
// step 4b -- allocate request virtqueue
//
Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect,
VIRTIO_SCSI_REQUEST_QUEUE);
Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);
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;
}
@@ -820,11 +825,24 @@ VirtioScsiInit (
}
//
// 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 4c -- Report GPFN (guest-physical frame number) of queue.
//
Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo,
(UINT32)(UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
@@ -834,8 +852,8 @@ VirtioScsiInit (
// the known (or unknown) VIRTIO_SCSI_F_* or VIRTIO_F_* capabilities (see
// virtio-0.9.5, Appendices B and I), except bidirectional transfers.
//
Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits,
Features & VIRTIO_SCSI_F_INOUT);
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo,
Features & VIRTIO_SCSI_F_INOUT);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
@@ -844,11 +862,11 @@ VirtioScsiInit (
// We expect these maximum sizes from the host. Since they are
// guest-negotiable, ask for them rather than just checking them.
//
Status = VIRTIO_CFG_WRITE (Dev, VhdrCdbSize, VIRTIO_SCSI_CDB_SIZE);
Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
Status = VIRTIO_CFG_WRITE (Dev, VhdrSenseSize, VIRTIO_SCSI_SENSE_SIZE);
Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);
if (EFI_ERROR (Status)) {
goto ReleaseQueue;
}
@@ -857,7 +875,7 @@ VirtioScsiInit (
// 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;
}
@@ -901,10 +919,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);
Dev->InOutSupported = FALSE;
Dev->MaxTarget = 0;
@@ -928,7 +946,7 @@ VirtioScsiUninit (
// 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);
Dev->InOutSupported = FALSE;
Dev->MaxTarget = 0;
@@ -953,11 +971,8 @@ VirtioScsiUninit (
// The implementation follows:
// - Driver Writer's Guide for UEFI 2.3.1 v1.01
// - 5.1.3.4 OpenProtocol() and CloseProtocol()
// - 18 PCI Driver Design Guidelines
// - 18.3 PCI drivers
// - UEFI Spec 2.3.1 + Errata C
// - 6.3 Protocol Handler Services
// - 13.4 EFI PCI I/O Protocol
//
EFI_STATUS
@@ -968,57 +983,37 @@ VirtioScsiDriverBindingSupported (
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
PCI_TYPE00 Pci;
EFI_STATUS Status;
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
// 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 == VIRTIO_SUBSYSTEM_SCSI_HOST) ? EFI_SUCCESS : EFI_UNSUPPORTED;
if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {
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;
}
@@ -1040,43 +1035,19 @@ VirtioScsiDriverBindingStart (
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 FreeVirtioScsi;
}
//
// 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.
//
// For virtio-scsi 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);
if (EFI_ERROR (Status)) {
goto ClosePciIo;
}
//
// PCI IO access granted, configure virtio-scsi device.
// VirtIo access granted, configure virtio-scsi device.
//
Status = VirtioScsiInit (Dev);
if (EFI_ERROR (Status)) {
goto RestorePciAttributes;
goto CloseVirtIo;
}
//
@@ -1096,12 +1067,8 @@ VirtioScsiDriverBindingStart (
UninitDev:
VirtioScsiUninit (Dev);
RestorePciAttributes:
Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
Dev->OriginalPciAttributes, NULL);
ClosePciIo:
gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
CloseVirtIo:
gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
This->DriverBindingHandle, DeviceHandle);
FreeVirtioScsi:
@@ -1149,10 +1116,7 @@ VirtioScsiDriverBindingStop (
VirtioScsiUninit (Dev);
Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
Dev->OriginalPciAttributes, NULL);
gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
This->DriverBindingHandle, DeviceHandle);
FreePool (Dev);