unreasonable for now.\r
\r
Copyright (C) 2012, Red Hat, Inc.\r
- Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\r
\r
This program and the accompanying materials are licensed and made available\r
under the terms and conditions of the BSD License which accompanies this\r
\r
**/\r
\r
-#include <IndustryStandard/Pci.h>\r
#include <IndustryStandard/VirtioScsi.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
\r
/**\r
\r
- Convenience macros to read and write region 0 IO space elements of the\r
- virtio-scsi PCI device, for configuration purposes.\r
+ Convenience macros to read and write configuration elements of the\r
+ virtio-scsi VirtIo device.\r
\r
The following macros make it possible to specify only the "core parameters"\r
for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()\r
returns, the transaction will have been completed.\r
\r
- @param[in] Dev Pointer to the VSCSI_DEV structure whose PCI IO space\r
- we're accessing. Dev->PciIo must be valid.\r
+ @param[in] Dev Pointer to the VSCSI_DEV structure.\r
\r
@param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi\r
configuration item to access.\r
one of UINT8, UINT16, UINT32, UINT64.\r
\r
\r
- @return Status codes returned by VirtioWrite() / VirtioRead().\r
+ @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice().\r
\r
**/\r
\r
-#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \\r
- (Dev)->PciIo, \\r
- OFFSET_OF_VSCSI (Field), \\r
- SIZE_OF_VSCSI (Field), \\r
- (Value) \\r
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \\r
+ (Dev)->VirtIo, \\r
+ OFFSET_OF_VSCSI (Field), \\r
+ SIZE_OF_VSCSI (Field), \\r
+ (Value) \\r
))\r
\r
-#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \\r
- (Dev)->PciIo, \\r
- OFFSET_OF_VSCSI (Field), \\r
- SIZE_OF_VSCSI (Field), \\r
- sizeof *(Pointer), \\r
- (Pointer) \\r
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \\r
+ (Dev)->VirtIo, \\r
+ OFFSET_OF_VSCSI (Field), \\r
+ SIZE_OF_VSCSI (Field), \\r
+ sizeof *(Pointer), \\r
+ (Pointer) \\r
))\r
\r
\r
// set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out\r
// parameter on return. The following is a full list of those fields, for\r
// easier validation of PopulateRequest(), ParseResponse(), and\r
-// VirtioScsiPassThru() below.\r
+// ReportHostAdapterError() below.\r
//\r
// - InTransferLength\r
// - OutTransferLength\r
//\r
Request->Lun[0] = 1;\r
Request->Lun[1] = (UINT8) Target;\r
- Request->Lun[2] = (UINT8) ((Lun >> 8) | 0x40);\r
+ Request->Lun[2] = (UINT8) (((UINT32)Lun >> 8) | 0x40);\r
Request->Lun[3] = (UINT8) Lun;\r
\r
//\r
}\r
\r
\r
+/**\r
+\r
+ The function can be used to create a fake host adapter error.\r
+\r
+ When VirtioScsiPassThru() is failed due to some reasons then this function\r
+ can be called to construct a host adapter error.\r
+\r
+ @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host\r
+ adapter error shall be placed in.\r
+\r
+\r
+ @retval EFI_DEVICE_ERROR The function returns this status code\r
+ unconditionally, to be propagated by\r
+ VirtioScsiPassThru().\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ReportHostAdapterError (\r
+ OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
+ )\r
+{\r
+ Packet->InTransferLength = 0;\r
+ Packet->OutTransferLength = 0;\r
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
+ Packet->SenseDataLength = 0;\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+\r
//\r
// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL\r
// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections\r
UINT16 TargetValue;\r
EFI_STATUS Status;\r
volatile VIRTIO_SCSI_REQ Request;\r
- volatile VIRTIO_SCSI_RESP Response;\r
+ volatile VIRTIO_SCSI_RESP *Response;\r
+ VOID *ResponseBuffer;\r
DESC_INDICES Indices;\r
+ VOID *RequestMapping;\r
+ VOID *ResponseMapping;\r
+ VOID *InDataMapping;\r
+ VOID *OutDataMapping;\r
+ EFI_PHYSICAL_ADDRESS RequestDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS InDataDeviceAddress;\r
+ EFI_PHYSICAL_ADDRESS OutDataDeviceAddress;\r
+ VOID *InDataBuffer;\r
+ UINTN InDataNumPages;\r
+ BOOLEAN OutDataBufferIsMapped;\r
\r
ZeroMem ((VOID*) &Request, sizeof (Request));\r
- ZeroMem ((VOID*) &Response, sizeof (Response));\r
\r
Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
CopyMem (&TargetValue, Target, sizeof TargetValue);\r
\r
+ InDataBuffer = NULL;\r
+ OutDataBufferIsMapped = FALSE;\r
+ InDataNumPages = 0;\r
+\r
Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- VirtioPrepare (&Dev->Ring, &Indices);\r
+ //\r
+ // Map the virtio-scsi Request header buffer\r
+ //\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterRead,\r
+ (VOID *) &Request,\r
+ sizeof Request,\r
+ &RequestDeviceAddress,\r
+ &RequestMapping);\r
+ if (EFI_ERROR (Status)) {\r
+ return ReportHostAdapterError (Packet);\r
+ }\r
+\r
+ //\r
+ // Map the input buffer\r
+ //\r
+ if (Packet->InTransferLength > 0) {\r
+ //\r
+ // Allocate a intermediate input buffer. This is mainly to handle the\r
+ // following case:\r
+ // * caller submits a bi-directional request\r
+ // * we perform the request fine\r
+ // * but we fail to unmap the "InDataMapping"\r
+ //\r
+ // In that case simply returing the EFI_DEVICE_ERROR is not sufficient. In\r
+ // addition to the error code we also need to update Packet fields\r
+ // accordingly so that we report the full loss of the incoming transfer.\r
+ //\r
+ // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If\r
+ // the Virtio request is successful then we copy the data from temporary\r
+ // buffer into Packet->InDataBuffer.\r
+ //\r
+ InDataNumPages = EFI_SIZE_TO_PAGES ((UINTN)Packet->InTransferLength);\r
+ Status = Dev->VirtIo->AllocateSharedPages (\r
+ Dev->VirtIo,\r
+ InDataNumPages,\r
+ &InDataBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapRequestBuffer;\r
+ }\r
+\r
+ ZeroMem (InDataBuffer, Packet->InTransferLength);\r
+\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ InDataBuffer,\r
+ Packet->InTransferLength,\r
+ &InDataDeviceAddress,\r
+ &InDataMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto FreeInDataBuffer;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Map the output buffer\r
+ //\r
+ if (Packet->OutTransferLength > 0) {\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterRead,\r
+ Packet->OutDataBuffer,\r
+ Packet->OutTransferLength,\r
+ &OutDataDeviceAddress,\r
+ &OutDataMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapInDataBuffer;\r
+ }\r
+\r
+ OutDataBufferIsMapped = TRUE;\r
+ }\r
+\r
+ //\r
+ // Response header is bi-direction (we preset with host status and expect\r
+ // the device to update it). Allocate a response buffer which can be mapped\r
+ // to access equally by both processor and device.\r
+ //\r
+ Status = Dev->VirtIo->AllocateSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *Response),\r
+ &ResponseBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapOutDataBuffer;\r
+ }\r
+\r
+ Response = ResponseBuffer;\r
+\r
+ ZeroMem ((VOID *)Response, sizeof (*Response));\r
\r
//\r
// preset a host status for ourselves that we do not accept as success\r
//\r
- Response.Response = VIRTIO_SCSI_S_FAILURE;\r
+ Response->Response = VIRTIO_SCSI_S_FAILURE;\r
+\r
+ //\r
+ // Map the response buffer with BusMasterCommonBuffer so that response\r
+ // buffer can be accessed by both host and device.\r
+ //\r
+ Status = VirtioMapAllBytesInSharedBuffer (\r
+ Dev->VirtIo,\r
+ VirtioOperationBusMasterCommonBuffer,\r
+ ResponseBuffer,\r
+ sizeof (*Response),\r
+ &ResponseDeviceAddress,\r
+ &ResponseMapping\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto FreeResponseBuffer;\r
+ }\r
+\r
+ VirtioPrepare (&Dev->Ring, &Indices);\r
\r
//\r
// ensured by VirtioScsiInit() -- this predicate, in combination with the\r
//\r
// enqueue Request\r
//\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,\r
- VRING_DESC_F_NEXT, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ RequestDeviceAddress,\r
+ sizeof Request,\r
+ VRING_DESC_F_NEXT,\r
+ &Indices\r
+ );\r
\r
//\r
// enqueue "dataout" if any\r
//\r
if (Packet->OutTransferLength > 0) {\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->OutDataBuffer,\r
- Packet->OutTransferLength, VRING_DESC_F_NEXT, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ OutDataDeviceAddress,\r
+ Packet->OutTransferLength,\r
+ VRING_DESC_F_NEXT,\r
+ &Indices\r
+ );\r
}\r
\r
//\r
// enqueue Response, to be written by the host\r
//\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Response, sizeof Response,\r
- VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ?\r
- VRING_DESC_F_NEXT : 0),\r
- &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ ResponseDeviceAddress,\r
+ sizeof *Response,\r
+ VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? VRING_DESC_F_NEXT : 0),\r
+ &Indices\r
+ );\r
\r
//\r
// enqueue "datain" if any, to be written by the host\r
//\r
if (Packet->InTransferLength > 0) {\r
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->InDataBuffer,\r
- Packet->InTransferLength, VRING_DESC_F_WRITE, &Indices);\r
+ VirtioAppendDesc (\r
+ &Dev->Ring,\r
+ InDataDeviceAddress,\r
+ Packet->InTransferLength,\r
+ VRING_DESC_F_WRITE,\r
+ &Indices\r
+ );\r
}\r
\r
// If kicking the host fails, we must fake a host adapter error.\r
// EFI_NOT_READY would save us the effort, but it would also suggest that the\r
// caller retry.\r
//\r
- if (VirtioFlush (Dev->PciIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,\r
- &Indices) != EFI_SUCCESS) {\r
- Packet->InTransferLength = 0;\r
- Packet->OutTransferLength = 0;\r
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
- Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
- Packet->SenseDataLength = 0;\r
- return EFI_DEVICE_ERROR;\r
+ if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,\r
+ &Indices, NULL) != EFI_SUCCESS) {\r
+ Status = ReportHostAdapterError (Packet);\r
+ goto UnmapResponseBuffer;\r
}\r
\r
- return ParseResponse (Packet, &Response);\r
+ Status = ParseResponse (Packet, Response);\r
+\r
+ //\r
+ // If virtio request was successful and it was a CPU read request then we\r
+ // have used an intermediate buffer. Copy the data from intermediate buffer\r
+ // to the final buffer.\r
+ //\r
+ if (InDataBuffer != NULL) {\r
+ CopyMem (Packet->InDataBuffer, InDataBuffer, Packet->InTransferLength);\r
+ }\r
+\r
+UnmapResponseBuffer:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, ResponseMapping);\r
+\r
+FreeResponseBuffer:\r
+ Dev->VirtIo->FreeSharedPages (\r
+ Dev->VirtIo,\r
+ EFI_SIZE_TO_PAGES (sizeof *Response),\r
+ ResponseBuffer\r
+ );\r
+\r
+UnmapOutDataBuffer:\r
+ if (OutDataBufferIsMapped) {\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, OutDataMapping);\r
+ }\r
+\r
+UnmapInDataBuffer:\r
+ if (InDataBuffer != NULL) {\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, InDataMapping);\r
+ }\r
+\r
+FreeInDataBuffer:\r
+ if (InDataBuffer != NULL) {\r
+ Dev->VirtIo->FreeSharedPages (Dev->VirtIo, InDataNumPages, InDataBuffer);\r
+ }\r
+\r
+UnmapRequestBuffer:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);\r
+\r
+ return Status;\r
}\r
\r
\r
{\r
UINT8 NextDevStat;\r
EFI_STATUS Status;\r
-\r
- UINT32 Features;\r
+ UINT64 RingBaseShift;\r
+ UINT64 Features;\r
UINT16 MaxChannel; // for validation only\r
UINT32 NumQueues; // for validation only\r
UINT16 QueueSize;\r
// Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.\r
//\r
NextDevStat = 0; // step 1 -- reset device\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
\r
NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
\r
NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // Set Page Size - MMIO VirtIo Specific\r
+ //\r
+ Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
//\r
// step 4a -- retrieve and validate features\r
//\r
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);\r
+ Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- Dev->InOutSupported = !!(Features & VIRTIO_SCSI_F_INOUT);\r
+ Dev->InOutSupported = (BOOLEAN) ((Features & VIRTIO_SCSI_F_INOUT) != 0);\r
\r
- Status = VIRTIO_CFG_READ (Dev, VhdrMaxChannel, &MaxChannel);\r
+ Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
goto Failed;\r
}\r
\r
- Status = VIRTIO_CFG_READ (Dev, VhdrNumQueues, &NumQueues);\r
+ Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
goto Failed;\r
}\r
\r
- Status = VIRTIO_CFG_READ (Dev, VhdrMaxTarget, &Dev->MaxTarget);\r
+ Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);\r
}\r
\r
- Status = VIRTIO_CFG_READ (Dev, VhdrMaxLun, &Dev->MaxLun);\r
+ Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);\r
}\r
\r
- Status = VIRTIO_CFG_READ (Dev, VhdrMaxSectors, &Dev->MaxSectors);\r
+ Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
goto Failed;\r
}\r
\r
+ Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 |\r
+ VIRTIO_F_IOMMU_PLATFORM;\r
+\r
+ //\r
+ // In virtio-1.0, feature negotiation is expected to complete before queue\r
+ // discovery, and the device can also reject the selected set of features.\r
+ //\r
+ if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+ Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Failed;\r
+ }\r
+ }\r
+\r
//\r
// step 4b -- allocate request virtqueue\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect,\r
- VIRTIO_SCSI_REQUEST_QUEUE);\r
+ Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
- Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize);\r
+ Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
goto Failed;\r
}\r
\r
- Status = VirtioRingInit (QueueSize, &Dev->Ring);\r
+ Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);\r
if (EFI_ERROR (Status)) {\r
goto Failed;\r
}\r
\r
//\r
- // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything\r
- // fails from here on, we must release the ring resources.\r
+ // If anything fails from here on, we must release the ring resources\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueAddress,\r
- (UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT);\r
+ Status = VirtioRingMap (\r
+ Dev->VirtIo,\r
+ &Dev->Ring,\r
+ &RingBaseShift,\r
+ &Dev->RingMap\r
+ );\r
if (EFI_ERROR (Status)) {\r
goto ReleaseQueue;\r
}\r
\r
//\r
- // step 5 -- Report understood features and guest-tuneables. We want none of\r
- // the known (or unknown) VIRTIO_SCSI_F_* or VIRTIO_F_* capabilities (see\r
- // virtio-0.9.5, Appendices B and I), except bidirectional transfers.\r
+ // Additional steps for MMIO: align the queue appropriately, and set the\r
+ // size. If anything fails from here on, we must unmap the ring resources.\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrGuestFeatureBits,\r
- Features & VIRTIO_SCSI_F_INOUT);\r
+ Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ //\r
+ // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
+ //\r
+ Status = Dev->VirtIo->SetQueueAddress (\r
+ Dev->VirtIo,\r
+ &Dev->Ring,\r
+ RingBaseShift\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
+\r
+ //\r
+ // step 5 -- Report understood features and guest-tuneables.\r
+ //\r
+ if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
+ Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
+ Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
+ if (EFI_ERROR (Status)) {\r
+ goto UnmapQueue;\r
+ }\r
}\r
\r
//\r
// We expect these maximum sizes from the host. Since they are\r
// guest-negotiable, ask for them rather than just checking them.\r
//\r
- Status = VIRTIO_CFG_WRITE (Dev, VhdrCdbSize, VIRTIO_SCSI_CDB_SIZE);\r
+ Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\r
}\r
- Status = VIRTIO_CFG_WRITE (Dev, VhdrSenseSize, VIRTIO_SCSI_SENSE_SIZE);\r
+ Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\r
}\r
\r
//\r
// step 6 -- initialization complete\r
//\r
NextDevStat |= VSTAT_DRIVER_OK;\r
- Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
if (EFI_ERROR (Status)) {\r
- goto ReleaseQueue;\r
+ goto UnmapQueue;\r
}\r
\r
//\r
\r
return EFI_SUCCESS;\r
\r
+UnmapQueue:\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
+\r
ReleaseQueue:\r
- VirtioRingUninit (&Dev->Ring);\r
+ VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
\r
Failed:\r
//\r
// Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device\r
- // Status. PCI IO access failure here should not mask the original error.\r
+ // Status. VirtIo access failure here should not mask the original error.\r
//\r
NextDevStat |= VSTAT_FAILED;\r
- VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
\r
Dev->InOutSupported = FALSE;\r
Dev->MaxTarget = 0;\r
}\r
\r
\r
-\r
STATIC\r
VOID\r
EFIAPI\r
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
// the old comms area.\r
//\r
- VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, 0);\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
\r
Dev->InOutSupported = FALSE;\r
Dev->MaxTarget = 0;\r
Dev->MaxLun = 0;\r
Dev->MaxSectors = 0;\r
\r
- VirtioRingUninit (&Dev->Ring);\r
+ Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
+ VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
\r
SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);\r
SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);\r
}\r
\r
\r
+//\r
+// Event notification function enqueued by ExitBootServices().\r
+//\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+VirtioScsiExitBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ VSCSI_DEV *Dev;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
+ //\r
+ // Reset the device. This causes the hypervisor to forget about the virtio\r
+ // ring.\r
+ //\r
+ // We allocated said ring in EfiBootServicesData type memory, and code\r
+ // executing after ExitBootServices() is permitted to overwrite it.\r
+ //\r
+ Dev = Context;\r
+ Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
+}\r
+\r
+\r
//\r
// Probe, start and stop functions of this driver, called by the DXE core for\r
// specific devices.\r
// The implementation follows:\r
// - Driver Writer's Guide for UEFI 2.3.1 v1.01\r
// - 5.1.3.4 OpenProtocol() and CloseProtocol()\r
-// - 18 PCI Driver Design Guidelines\r
-// - 18.3 PCI drivers\r
// - UEFI Spec 2.3.1 + Errata C\r
// - 6.3 Protocol Handler Services\r
-// - 13.4 EFI PCI I/O Protocol\r
//\r
\r
EFI_STATUS\r
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_PCI_IO_PROTOCOL *PciIo;\r
- PCI_TYPE00 Pci;\r
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
\r
//\r
- // Attempt to open the device with the PciIo set of interfaces. On success,\r
- // the protocol is "instantiated" for the PCI device. Covers duplicate open\r
+ // Attempt to open the device with the VirtIo set of interfaces. On success,\r
+ // the protocol is "instantiated" for the VirtIo device. Covers duplicate open\r
// attempts (EFI_ALREADY_STARTED).\r
//\r
Status = gBS->OpenProtocol (\r
DeviceHandle, // candidate device\r
- &gEfiPciIoProtocolGuid, // for generic PCI access\r
- (VOID **)&PciIo, // handle to instantiate\r
+ &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
+ (VOID **)&VirtIo, // handle to instantiate\r
This->DriverBindingHandle, // requestor driver identity\r
DeviceHandle, // ControllerHandle, according to\r
// the UEFI Driver Model\r
- EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive PciIo access to\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to\r
// the device; to be released\r
);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- //\r
- // Read entire PCI configuration header for more extensive check ahead.\r
- //\r
- Status = PciIo->Pci.Read (\r
- PciIo, // (protocol, device)\r
- // handle\r
- EfiPciIoWidthUint32, // access width & copy\r
- // mode\r
- 0, // Offset\r
- sizeof Pci / sizeof (UINT32), // Count\r
- &Pci // target buffer\r
- );\r
-\r
- if (Status == EFI_SUCCESS) {\r
- //\r
- // virtio-0.9.5, 2.1 PCI Discovery\r
- //\r
- Status = (Pci.Hdr.VendorId == 0x1AF4 &&\r
- Pci.Hdr.DeviceId >= 0x1000 && Pci.Hdr.DeviceId <= 0x103F &&\r
- Pci.Hdr.RevisionID == 0x00 &&\r
- Pci.Device.SubsystemID == 0x08) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
+ if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {\r
+ Status = EFI_UNSUPPORTED;\r
}\r
\r
//\r
- // We needed PCI IO access only transitorily, to see whether we support the\r
+ // We needed VirtIo access only transitorily, to see whether we support the\r
// device or not.\r
//\r
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
This->DriverBindingHandle, DeviceHandle);\r
return Status;\r
}\r
return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
- (VOID **)&Dev->PciIo, This->DriverBindingHandle,\r
+ Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
+ (VOID **)&Dev->VirtIo, This->DriverBindingHandle,\r
DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
if (EFI_ERROR (Status)) {\r
goto FreeVirtioScsi;\r
}\r
\r
//\r
- // We must retain and ultimately restore the original PCI attributes of the\r
- // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /\r
- // 18.3.2 Start() and Stop().\r
- //\r
- // The third parameter ("Attributes", input) is ignored by the Get operation.\r
- // The fourth parameter ("Result", output) is ignored by the Enable and Set\r
- // operations.\r
+ // VirtIo access granted, configure virtio-scsi device.\r
//\r
- // For virtio-scsi we only need IO space access.\r
- //\r
- Status = Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationGet,\r
- 0, &Dev->OriginalPciAttributes);\r
- if (EFI_ERROR (Status)) {\r
- goto ClosePciIo;\r
- }\r
-\r
- Status = Dev->PciIo->Attributes (Dev->PciIo,\r
- EfiPciIoAttributeOperationEnable,\r
- EFI_PCI_IO_ATTRIBUTE_IO, NULL);\r
+ Status = VirtioScsiInit (Dev);\r
if (EFI_ERROR (Status)) {\r
- goto ClosePciIo;\r
+ goto CloseVirtIo;\r
}\r
\r
- //\r
- // PCI IO access granted, configure virtio-scsi device.\r
- //\r
- Status = VirtioScsiInit (Dev);\r
+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,\r
+ &VirtioScsiExitBoot, Dev, &Dev->ExitBoot);\r
if (EFI_ERROR (Status)) {\r
- goto RestorePciAttributes;\r
+ goto UninitDev;\r
}\r
\r
//\r
&gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,\r
&Dev->PassThru);\r
if (EFI_ERROR (Status)) {\r
- goto UninitDev;\r
+ goto CloseExitBoot;\r
}\r
\r
return EFI_SUCCESS;\r
\r
+CloseExitBoot:\r
+ gBS->CloseEvent (Dev->ExitBoot);\r
+\r
UninitDev:\r
VirtioScsiUninit (Dev);\r
\r
-RestorePciAttributes:\r
- Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
- Dev->OriginalPciAttributes, NULL);\r
-\r
-ClosePciIo:\r
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+CloseVirtIo:\r
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
This->DriverBindingHandle, DeviceHandle);\r
\r
FreeVirtioScsi:\r
return Status;\r
}\r
\r
- VirtioScsiUninit (Dev);\r
+ gBS->CloseEvent (Dev->ExitBoot);\r
\r
- Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,\r
- Dev->OriginalPciAttributes, NULL);\r
+ VirtioScsiUninit (Dev);\r
\r
- gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
+ gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
This->DriverBindingHandle, DeviceHandle);\r
\r
FreePool (Dev);\r