summaryrefslogtreecommitdiffstats
path: root/OvmfPkg
diff options
context:
space:
mode:
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2012-10-18 17:07:48 +0000
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2012-10-18 17:07:48 +0000
commit37078a63b1911f8b320bab6d82a2183a84f8858c (patch)
tree24a41498cbc71c56d911d353a1e8f353a7b8ba43 /OvmfPkg
parentc8c2e4d61339592056931dff5c209b7d06fb466c (diff)
downloadedk2-37078a63b1911f8b320bab6d82a2183a84f8858c.tar.gz
edk2-37078a63b1911f8b320bab6d82a2183a84f8858c.tar.bz2
edk2-37078a63b1911f8b320bab6d82a2183a84f8858c.zip
OvmfPkg: introduce virtio-scsi driver
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Jordan Justen <jordan.l.justen@intel.com> [jordan.l.justen@intel.com: fix build for VS2012] Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen <jordan.l.justen@intel.com> git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13867 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'OvmfPkg')
-rw-r--r--OvmfPkg/Include/IndustryStandard/VirtioScsi.h100
-rw-r--r--OvmfPkg/OvmfPkg.dec11
-rw-r--r--OvmfPkg/OvmfPkgIa32.dsc1
-rw-r--r--OvmfPkg/OvmfPkgIa32.fdf1
-rw-r--r--OvmfPkg/OvmfPkgIa32X64.dsc1
-rw-r--r--OvmfPkg/OvmfPkgIa32X64.fdf1
-rw-r--r--OvmfPkg/OvmfPkgX64.dsc1
-rw-r--r--OvmfPkg/OvmfPkgX64.fdf1
-rw-r--r--OvmfPkg/VirtioScsiDxe/VirtioScsi.c1275
-rw-r--r--OvmfPkg/VirtioScsiDxe/VirtioScsi.h209
-rw-r--r--OvmfPkg/VirtioScsiDxe/VirtioScsi.inf47
11 files changed, 1648 insertions, 0 deletions
diff --git a/OvmfPkg/Include/IndustryStandard/VirtioScsi.h b/OvmfPkg/Include/IndustryStandard/VirtioScsi.h
new file mode 100644
index 0000000000..59ce97e070
--- /dev/null
+++ b/OvmfPkg/Include/IndustryStandard/VirtioScsi.h
@@ -0,0 +1,100 @@
+/** @file
+
+ Virtio SCSI Host Device specific type and macro definitions corresponding to
+ the virtio-0.9.5 specification.
+
+ Copyright (C) 2012, Red Hat, Inc.
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _VIRTIO_SCSI_H_
+#define _VIRTIO_SCSI_H_
+
+#include <IndustryStandard/Virtio.h>
+
+
+//
+// virtio-0.9.5, Appendix I: SCSI Host Device
+//
+#pragma pack(1)
+typedef struct {
+ VIRTIO_HDR Generic;
+ UINT32 VhdrNumQueues;
+ UINT32 VhdrSegMax;
+ UINT32 VhdrMaxSectors;
+ UINT32 VhdrCmdPerLun;
+ UINT32 VhdrEventInfoSize;
+ UINT32 VhdrSenseSize;
+ UINT32 VhdrCdbSize;
+ UINT16 VhdrMaxChannel;
+ UINT16 VhdrMaxTarget;
+ UINT32 VhdrMaxLun;
+} VSCSI_HDR;
+#pragma pack()
+
+#define OFFSET_OF_VSCSI(Field) OFFSET_OF (VSCSI_HDR, Field)
+#define SIZE_OF_VSCSI(Field) (sizeof ((VSCSI_HDR *) 0)->Field)
+
+#define VIRTIO_SCSI_F_INOUT BIT0
+#define VIRTIO_SCSI_F_HOTPLUG BIT1
+
+//
+// We expect these maximum sizes from the host. Also we force the CdbLength and
+// SenseDataLength parameters of EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() not
+// to exceed these limits. See UEFI 2.3.1 errata C 14.7.
+//
+#define VIRTIO_SCSI_CDB_SIZE 32
+#define VIRTIO_SCSI_SENSE_SIZE 96
+
+//
+// We pass the dynamically sized buffers ("dataout", "datain") in separate ring
+// descriptors.
+//
+#pragma pack(1)
+typedef struct {
+ UINT8 Lun[8];
+ UINT64 Id;
+ UINT8 TaskAttr;
+ UINT8 Prio;
+ UINT8 Crn;
+ UINT8 Cdb[VIRTIO_SCSI_CDB_SIZE];
+} VIRTIO_SCSI_REQ;
+
+typedef struct {
+ UINT32 SenseLen;
+ UINT32 Residual;
+ UINT16 StatusQualifier;
+ UINT8 Status;
+ UINT8 Response;
+ UINT8 Sense[VIRTIO_SCSI_SENSE_SIZE];
+} VIRTIO_SCSI_RESP;
+#pragma pack()
+
+//
+// selector of first virtio queue usable for request transfer
+//
+#define VIRTIO_SCSI_REQUEST_QUEUE 2
+
+//
+// host response codes
+//
+#define VIRTIO_SCSI_S_OK 0
+#define VIRTIO_SCSI_S_OVERRUN 1
+#define VIRTIO_SCSI_S_ABORTED 2
+#define VIRTIO_SCSI_S_BAD_TARGET 3
+#define VIRTIO_SCSI_S_RESET 4
+#define VIRTIO_SCSI_S_BUSY 5
+#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
+#define VIRTIO_SCSI_S_TARGET_FAILURE 7
+#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
+#define VIRTIO_SCSI_S_FAILURE 9
+
+#endif // _VIRTIO_SCSI_H_
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 26600dcf42..73db6af772 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -53,6 +53,17 @@
# to PIIX4 function 3 offset 0x40-0x43 bits [15:6].
gUefiOvmfPkgTokenSpaceGuid.PcdAcpiPmBaseAddress|0xB000|UINT16|5
+ ## When VirtioScsiDxe is instantiated for a HBA, the numbers of targets and
+ # LUNs are retrieved from the host during virtio-scsi setup.
+ # MdeModulePkg/Bus/Scsi/ScsiBusDxe then scans all MaxTarget * MaxLun
+ # possible devices. This can take extremely long, for example with
+ # MaxTarget=255 and MaxLun=16383. The *inclusive* constants below limit
+ # MaxTarget and MaxLun, independently, should the host report higher values,
+ # so that scanning the number of devices given by their product is still
+ # acceptably fast.
+ gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxTargetLimit|31|UINT16|6
+ gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxLunLimit|7|UINT32|7
+
[PcdsDynamic, PcdsDynamicEx]
gUefiOvmfPkgTokenSpaceGuid.PcdEmuVariableEvent|0|UINT64|2
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index d0c632979d..bd86572413 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -409,6 +409,7 @@
OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+ OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {
<LibraryClasses>
PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index be5e6d8deb..36c9756656 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -181,6 +181,7 @@ INF PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
INF OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
INF OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+INF OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
INF OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index b442314680..0a1609f313 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -416,6 +416,7 @@
OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+ OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {
<LibraryClasses>
PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 07faec49fe..a762dc9f21 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -181,6 +181,7 @@ INF PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
INF OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
INF OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+INF OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
INF OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 11f90a4818..b24d1b9df0 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -414,6 +414,7 @@
OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+ OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf {
<LibraryClasses>
PlatformFvbLib|OvmfPkg/Library/EmuVariableFvbLib/EmuVariableFvbLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 615bc0a1c2..fcdac0316b 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -181,6 +181,7 @@ INF PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcatRealTimeClockRuntimeDxe.inf
INF OvmfPkg/BlockMmioToBlockIoDxe/BlockIo.inf
INF OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+INF OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
INF OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.inf
INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
diff --git a/OvmfPkg/VirtioScsiDxe/VirtioScsi.c b/OvmfPkg/VirtioScsiDxe/VirtioScsi.c
new file mode 100644
index 0000000000..66f6d31d74
--- /dev/null
+++ b/OvmfPkg/VirtioScsiDxe/VirtioScsi.c
@@ -0,0 +1,1275 @@
+/** @file
+
+ This driver produces Extended SCSI Pass Thru Protocol instances for
+ virtio-scsi devices.
+
+ The implementation is basic:
+
+ - No hotplug / hot-unplug.
+
+ - Although EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() could be a good match
+ for multiple in-flight virtio-scsi requests, we stick to synchronous
+ requests for now.
+
+ - Timeouts are not supported for EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru().
+
+ - Only one channel is supported. (At the time of this writing, host-side
+ virtio-scsi supports a single channel too.)
+
+ - Only one request queue is used (for the one synchronous request).
+
+ - The ResetChannel() and ResetTargetLun() functions of
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL are not supported (which is allowed by the
+ UEFI 2.3.1 Errata C specification), although
+ VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET could be a good match. That would
+ however require client code for the control queue, which is deemed
+ unreasonable for now.
+
+ Copyright (C) 2012, Red Hat, Inc.
+ Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/VirtioScsi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/VirtioLib.h>
+
+#include "VirtioScsi.h"
+
+/**
+
+ Convenience macros to read and write region 0 IO space elements of the
+ virtio-scsi PCI 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 VSCSI_DEV structure whose PCI IO space
+ we're accessing. Dev->PciIo must be valid.
+
+ @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
+ configuration item to access.
+
+ @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the
+ selected configuration item.
+
+ @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the
+ value read from the configuration item. Its type must be
+ one of UINT8, UINT16, UINT32, UINT64.
+
+
+ @return Status codes returned by VirtioWrite() / VirtioRead().
+
+**/
+
+#define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \
+ (Dev)->PciIo, \
+ OFFSET_OF_VSCSI (Field), \
+ SIZE_OF_VSCSI (Field), \
+ (Value) \
+ ))
+
+#define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \
+ (Dev)->PciIo, \
+ OFFSET_OF_VSCSI (Field), \
+ SIZE_OF_VSCSI (Field), \
+ sizeof *(Pointer), \
+ (Pointer) \
+ ))
+
+
+//
+// UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
+// the PassThru() interface. Beside returning a status code, the function must
+// set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
+// parameter on return. The following is a full list of those fields, for
+// easier validation of PopulateRequest(), ParseResponse(), and
+// VirtioScsiPassThru() below.
+//
+// - InTransferLength
+// - OutTransferLength
+// - HostAdapterStatus
+// - TargetStatus
+// - SenseDataLength
+// - SenseData
+//
+// On any return from the PassThru() interface, these fields must be set,
+// except if the returned status code is explicitly exempt. (Actually the
+// implementation here conservatively sets these fields even in case not all
+// of them would be required by the specification.)
+//
+
+/**
+
+ Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
+ packet.
+
+ The caller is responsible for pre-zeroing the virtio-scsi request. The
+ Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
+ by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
+
+ @param[in] Dev The virtio-scsi host device the packet targets.
+
+ @param[in] Target The SCSI target controlled by the virtio-scsi host
+ device.
+
+ @param[in] Lun The Logical Unit Number under the SCSI target.
+
+ @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
+ function translates to a virtio-scsi request. On
+ failure this parameter relays error contents.
+
+ @param[out] Request The pre-zeroed virtio-scsi request to populate. This
+ parameter is volatile-qualified because we expect the
+ caller to append it to a virtio ring, thus
+ assignments to Request must be visible when the
+ function returns.
+
+
+ @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
+ Request has been populated.
+
+ @return Otherwise, invalid or unsupported parameters were
+ detected. Status codes are meant for direct forwarding
+ by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
+ implementation.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PopulateRequest (
+ IN CONST VSCSI_DEV *Dev,
+ IN UINT16 Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ OUT volatile VIRTIO_SCSI_REQ *Request
+ )
+{
+ UINTN Idx;
+
+ if (
+ //
+ // bidirectional transfer was requested, but the host doesn't support it
+ //
+ (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0 &&
+ !Dev->InOutSupported) ||
+
+ //
+ // a target / LUN was addressed that's impossible to encode for the host
+ //
+ Target > 0xFF || Lun >= 0x4000 ||
+
+ //
+ // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
+ //
+ Packet->CdbLength > VIRTIO_SCSI_CDB_SIZE ||
+
+ //
+ // From virtio-0.9.5, 2.3.2 Descriptor Table:
+ // "no descriptor chain may be more than 2^32 bytes long in total".
+ //
+ (UINT64) Packet->InTransferLength + Packet->OutTransferLength > SIZE_1GB
+ ) {
+
+ //
+ // this error code doesn't require updates to the Packet output fields
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ if (
+ //
+ // addressed invalid device
+ //
+ Target > Dev->MaxTarget || Lun > Dev->MaxLun ||
+
+ //
+ // invalid direction (there doesn't seem to be a macro for the "no data
+ // transferred" "direction", eg. for TEST UNIT READY)
+ //
+ Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
+
+ //
+ // trying to receive, but destination pointer is NULL, or contradicting
+ // transfer direction
+ //
+ (Packet->InTransferLength > 0 &&
+ (Packet->InDataBuffer == NULL ||
+ Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
+ )
+ ) ||
+
+ //
+ // trying to send, but source pointer is NULL, or contradicting transfer
+ // direction
+ //
+ (Packet->OutTransferLength > 0 &&
+ (Packet->OutDataBuffer == NULL ||
+ Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
+ )
+ )
+ ) {
+
+ //
+ // this error code doesn't require updates to the Packet output fields
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Catch oversized requests eagerly. If this condition evaluates to false,
+ // then the combined size of a bidirectional request will not exceed the
+ // virtio-scsi device's transfer limit either.
+ //
+ if (ALIGN_VALUE (Packet->OutTransferLength, 512) / 512
+ > Dev->MaxSectors / 2 ||
+ ALIGN_VALUE (Packet->InTransferLength, 512) / 512
+ > Dev->MaxSectors / 2) {
+ Packet->InTransferLength = (Dev->MaxSectors / 2) * 512;
+ Packet->OutTransferLength = (Dev->MaxSectors / 2) * 512;
+ Packet->HostAdapterStatus =
+ EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+ Packet->SenseDataLength = 0;
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ //
+ // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,
+ // Device Operation: request queues
+ //
+ Request->Lun[0] = 1;
+ Request->Lun[1] = (UINT8) Target;
+ Request->Lun[2] = (UINT8) ((Lun >> 8) | 0x40);
+ Request->Lun[3] = (UINT8) Lun;
+
+ //
+ // CopyMem() would cast away the "volatile" qualifier before access, which is
+ // undefined behavior (ISO C99 6.7.3p5)
+ //
+ for (Idx = 0; Idx < Packet->CdbLength; ++Idx) {
+ Request->Cdb[Idx] = ((UINT8 *) Packet->Cdb)[Idx];
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+
+ Parse the virtio-scsi device's response, translate it to an EFI status code,
+ and update the Extended SCSI Pass Thru Protocol packet, to be returned by
+ the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
+
+ @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
+ been translated to a virtio-scsi request with
+ PopulateRequest(), and processed by the host. On
+ output this parameter is updated with response or
+ error contents.
+
+ @param[in] Response The virtio-scsi response structure to parse. We expect
+ it to come from a virtio ring, thus it is qualified
+ volatile.
+
+
+ @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
+ Extended SCSI Pass Thru Protocol.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ParseResponse (
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN CONST volatile VIRTIO_SCSI_RESP *Response
+ )
+{
+ UINTN ResponseSenseLen;
+ UINTN Idx;
+
+ //
+ // return sense data (length and contents) in all cases, truncated if needed
+ //
+ ResponseSenseLen = MIN (Response->SenseLen, VIRTIO_SCSI_SENSE_SIZE);
+ if (Packet->SenseDataLength > ResponseSenseLen) {
+ Packet->SenseDataLength = (UINT8) ResponseSenseLen;
+ }
+ for (Idx = 0; Idx < Packet->SenseDataLength; ++Idx) {
+ ((UINT8 *) Packet->SenseData)[Idx] = Response->Sense[Idx];
+ }
+
+ //
+ // Report actual transfer lengths. The logic below covers all three
+ // DataDirections (read, write, bidirectional).
+ //
+ // -+- @ 0
+ // |
+ // | write ^ @ Residual (unprocessed)
+ // | |
+ // -+- @ OutTransferLength -+- @ InTransferLength
+ // | |
+ // | read |
+ // | |
+ // V @ OutTransferLength + InTransferLength -+- @ 0
+ //
+ if (Response->Residual <= Packet->InTransferLength) {
+ Packet->InTransferLength -= Response->Residual;
+ }
+ else {
+ Packet->OutTransferLength -= Response->Residual - Packet->InTransferLength;
+ Packet->InTransferLength = 0;
+ }
+
+ //
+ // report target status in all cases
+ //
+ Packet->TargetStatus = Response->Status;
+
+ //
+ // host adapter status and function return value depend on virtio-scsi
+ // response code
+ //
+ switch (Response->Response) {
+ case VIRTIO_SCSI_S_OK:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+ return EFI_SUCCESS;
+
+ case VIRTIO_SCSI_S_OVERRUN:
+ Packet->HostAdapterStatus =
+ EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
+ break;
+
+ case VIRTIO_SCSI_S_BAD_TARGET:
+ //
+ // This is non-intuitive but explicitly required by the
+ // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
+ // disconnected (but otherwise valid) target / LUN addresses.
+ //
+ Packet->HostAdapterStatus =
+ EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;
+ return EFI_TIMEOUT;
+
+ case VIRTIO_SCSI_S_RESET:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;
+ break;
+
+ case VIRTIO_SCSI_S_BUSY:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
+ return EFI_NOT_READY;
+
+ //
+ // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
+ // intentional as well, not an oversight.
+ //
+ case VIRTIO_SCSI_S_ABORTED:
+ case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
+ case VIRTIO_SCSI_S_TARGET_FAILURE:
+ case VIRTIO_SCSI_S_NEXUS_FAILURE:
+ case VIRTIO_SCSI_S_FAILURE:
+ default:
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+ }
+
+ return EFI_DEVICE_ERROR;
+}
+
+
+//
+// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
+// - 14.1 SCSI Driver Model Overview,
+// - 14.7 Extended SCSI Pass Thru Protocol.
+//
+
+EFI_STATUS
+EFIAPI
+VirtioScsiPassThru (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ VSCSI_DEV *Dev;
+ UINT16 TargetValue;
+ EFI_STATUS Status;
+ volatile VIRTIO_SCSI_REQ Request;
+ volatile VIRTIO_SCSI_RESP Response;
+ DESC_INDICES Indices;
+
+ //
+ // Zero-initialization of Request & Response with "= { 0 };" doesn't build
+ // with gcc-4.4: "undefined reference to `memset'". Direct SetMem() is not
+ // allowed as it would cast away the volatile qualifier. Work it around.
+ //
+ union {
+ VIRTIO_SCSI_REQ Request;
+ VIRTIO_SCSI_RESP Response;
+ } Zero;
+
+ SetMem (&Zero, sizeof Zero, 0x00);
+ Request = Zero.Request;
+ Response = Zero.Response;
+
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
+ CopyMem (&TargetValue, Target, sizeof TargetValue);
+
+ Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VirtioPrepare (&Dev->Ring, &Indices);
+
+ //
+ // preset a host status for ourselves that we do not accept as success
+ //
+ Response.Response = VIRTIO_SCSI_S_FAILURE;
+
+ //
+ // ensured by VirtioScsiInit() -- this predicate, in combination with the
+ // lock-step progress, ensures we don't have to track free descriptors.
+ //
+ ASSERT (Dev->Ring.QueueSize >= 4);
+
+ //
+ // enqueue Request
+ //
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
+ VRING_DESC_F_NEXT, &Indices);
+
+ //
+ // enqueue "dataout" if any
+ //
+ if (Packet->OutTransferLength > 0) {
+ VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->OutDataBuffer,
+ Packet->OutTransferLength, VRING_DESC_F_NEXT, &Indices);
+ }
+
+ //
+ // enqueue Response, to be written by the host
+ //
+ VirtioAppendDesc (&Dev->Ring, (UINTN) &Response, sizeof Response,
+ VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ?
+ VRING_DESC_F_NEXT : 0),
+ &Indices);
+
+ //
+ // enqueue "datain" if any, to be written by the host
+ //
+ if (Packet->InTransferLength > 0) {
+ VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->InDataBuffer,
+ Packet->InTransferLength, VRING_DESC_F_WRITE, &Indices);
+ }
+
+ // If kicking the host fails, we must fake a host adapter error.
+ // 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,
+ &Indices) != EFI_SUCCESS) {
+ Packet->InTransferLength = 0;
+ Packet->OutTransferLength = 0;
+ Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
+ Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
+ Packet->SenseDataLength = 0;
+ return EFI_DEVICE_ERROR;
+ }
+
+ return ParseResponse (Packet, &Response);
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **TargetPointer,
+ IN OUT UINT64 *Lun
+ )
+{
+ UINT8 *Target;
+ UINTN Idx;
+ UINT16 LastTarget;
+ VSCSI_DEV *Dev;
+
+ //
+ // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
+ //
+ Target = *TargetPointer;
+
+ //
+ // Search for first non-0xFF byte. If not found, return first target & LUN.
+ //
+ for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
+ ;
+ if (Idx == TARGET_MAX_BYTES) {
+ SetMem (Target, TARGET_MAX_BYTES, 0x00);
+ *Lun = 0;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
+ //
+ CopyMem (&LastTarget, Target, sizeof LastTarget);
+
+ //
+ // increment (target, LUN) pair if valid on input
+ //
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
+ if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*Lun < Dev->MaxLun) {
+ ++*Lun;
+ return EFI_SUCCESS;
+ }
+
+ if (LastTarget < Dev->MaxTarget) {
+ *Lun = 0;
+ ++LastTarget;
+ CopyMem (Target, &LastTarget, sizeof LastTarget);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ UINT16 TargetValue;
+ VSCSI_DEV *Dev;
+ SCSI_DEVICE_PATH *ScsiDevicePath;
+
+ if (DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (&TargetValue, Target, sizeof TargetValue);
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
+ if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) {
+ return EFI_NOT_FOUND;
+ }
+
+ ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);
+ if (ScsiDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
+ ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
+ ScsiDevicePath->Header.Length[0] = (UINT8) sizeof *ScsiDevicePath;
+ ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8);
+ ScsiDevicePath->Pun = TargetValue;
+ ScsiDevicePath->Lun = (UINT16) Lun;
+
+ *DevicePath = &ScsiDevicePath->Header;
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **TargetPointer,
+ OUT UINT64 *Lun
+ )
+{
+ SCSI_DEVICE_PATH *ScsiDevicePath;
+ VSCSI_DEV *Dev;
+ UINT8 *Target;
+
+ if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL ||
+ Lun == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
+ DevicePath->SubType != MSG_SCSI_DP) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath;
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
+ if (ScsiDevicePath->Pun > Dev->MaxTarget ||
+ ScsiDevicePath->Lun > Dev->MaxLun) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
+ // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
+ // c) ScsiDevicePath->Pun is an UINT16
+ //
+ Target = *TargetPointer;
+ CopyMem (Target, &ScsiDevicePath->Pun, 2);
+ SetMem (Target + 2, TARGET_MAX_BYTES - 2, 0x00);
+
+ *Lun = ScsiDevicePath->Lun;
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **TargetPointer
+ )
+{
+ UINT8 *Target;
+ UINTN Idx;
+ UINT16 LastTarget;
+ VSCSI_DEV *Dev;
+
+ //
+ // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
+ //
+ Target = *TargetPointer;
+
+ //
+ // Search for first non-0xFF byte. If not found, return first target.
+ //
+ for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
+ ;
+ if (Idx == TARGET_MAX_BYTES) {
+ SetMem (Target, TARGET_MAX_BYTES, 0x00);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
+ //
+ CopyMem (&LastTarget, Target, sizeof LastTarget);
+
+ //
+ // increment target if valid on input
+ //
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
+ if (LastTarget > Dev->MaxTarget) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (LastTarget < Dev->MaxTarget) {
+ ++LastTarget;
+ CopyMem (Target, &LastTarget, sizeof LastTarget);
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+VirtioScsiInit (
+ IN OUT VSCSI_DEV *Dev
+ )
+{
+ UINT8 NextDevStat;
+ EFI_STATUS Status;
+
+ UINT32 Features;
+ UINT16 MaxChannel; // for validation only
+ UINT32 NumQueues; // for validation only
+ UINT16 QueueSize;
+
+ //
+ // 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);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, 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);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // step 4a -- retrieve and validate features
+ //
+ Status = VIRTIO_CFG_READ (Dev, Generic.VhdrDeviceFeatureBits, &Features);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ Dev->InOutSupported = !!(Features & VIRTIO_SCSI_F_INOUT);
+
+ Status = VIRTIO_CFG_READ (Dev, VhdrMaxChannel, &MaxChannel);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (MaxChannel != 0) {
+ //
+ // this driver is for a single-channel virtio-scsi HBA
+ //
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, VhdrNumQueues, &NumQueues);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (NumQueues < 1) {
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, VhdrMaxTarget, &Dev->MaxTarget);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (Dev->MaxTarget > PcdGet16 (PcdVirtioScsiMaxTargetLimit)) {
+ Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, VhdrMaxLun, &Dev->MaxLun);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (Dev->MaxLun > PcdGet32 (PcdVirtioScsiMaxLunLimit)) {
+ Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);
+ }
+
+ Status = VIRTIO_CFG_READ (Dev, VhdrMaxSectors, &Dev->MaxSectors);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ if (Dev->MaxSectors < 2) {
+ //
+ // We must be able to halve it for bidirectional transfers
+ // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
+ //
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ //
+ // step 4b -- allocate request virtqueue
+ //
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueSelect,
+ VIRTIO_SCSI_REQUEST_QUEUE);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ Status = VIRTIO_CFG_READ (Dev, Generic.VhdrQueueSize, &QueueSize);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+ //
+ // VirtioScsiPassThru() uses at most four descriptors
+ //
+ if (QueueSize < 4) {
+ Status = EFI_UNSUPPORTED;
+ goto Failed;
+ }
+
+ Status = VirtioRingInit (QueueSize, &Dev->Ring);
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ //
+ // step 4c -- Report GPFN (guest-physical frame number) of queue. 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);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ //
+ // step 5 -- Report understood features and guest-tuneables. We want none of
+ // 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);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ //
+ // 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);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+ Status = VIRTIO_CFG_WRITE (Dev, VhdrSenseSize, VIRTIO_SCSI_SENSE_SIZE);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ //
+ // step 6 -- initialization complete
+ //
+ NextDevStat |= VSTAT_DRIVER_OK;
+ Status = VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+ if (EFI_ERROR (Status)) {
+ goto ReleaseQueue;
+ }
+
+ //
+ // populate the exported interface's attributes
+ //
+ Dev->PassThru.Mode = &Dev->PassThruMode;
+ Dev->PassThru.PassThru = &VirtioScsiPassThru;
+ Dev->PassThru.GetNextTargetLun = &VirtioScsiGetNextTargetLun;
+ Dev->PassThru.BuildDevicePath = &VirtioScsiBuildDevicePath;
+ Dev->PassThru.GetTargetLun = &VirtioScsiGetTargetLun;
+ Dev->PassThru.ResetChannel = &VirtioScsiResetChannel;
+ Dev->PassThru.ResetTargetLun = &VirtioScsiResetTargetLun;
+ Dev->PassThru.GetNextTarget = &VirtioScsiGetNextTarget;
+
+ //
+ // AdapterId is a target for which no handle will be created during bus scan.
+ // Prevent any conflict with real devices.
+ //
+ Dev->PassThruMode.AdapterId = 0xFFFFFFFF;
+
+ //
+ // Set both physical and logical attributes for non-RAID SCSI channel. See
+ // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
+ // SCSI Pass Thru Protocol.
+ //
+ Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
+ EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+
+ //
+ // no restriction on transfer buffer alignment
+ //
+ Dev->PassThruMode.IoAlign = 0;
+
+ return EFI_SUCCESS;
+
+ReleaseQueue:
+ VirtioRingUninit (&Dev->Ring);
+
+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.
+ //
+ NextDevStat |= VSTAT_FAILED;
+ VIRTIO_CFG_WRITE (Dev, Generic.VhdrDeviceStatus, NextDevStat);
+
+ Dev->InOutSupported = FALSE;
+ Dev->MaxTarget = 0;
+ Dev->MaxLun = 0;
+ Dev->MaxSectors = 0;
+
+ return Status; // reached only via Failed above
+}
+
+
+
+STATIC
+VOID
+EFIAPI
+VirtioScsiUninit (
+ IN OUT VSCSI_DEV *Dev
+ )
+{
+ //
+ // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
+ // 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->InOutSupported = FALSE;
+ Dev->MaxTarget = 0;
+ Dev->MaxLun = 0;
+ Dev->MaxSectors = 0;
+
+ VirtioRingUninit (&Dev->Ring);
+
+ SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);
+ SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);
+}
+
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+// 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
+EFIAPI
+VirtioScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ //
+ // 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).
+ //
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gEfiPciIoProtocolGuid, // for generic PCI access
+ (VOID **)&PciIo, // 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
+ // 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 == 0x08) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+ }
+
+ //
+ // We needed PCI IO access only transitorily, to see whether we support the
+ // device or not.
+ //
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+ return Status;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ VSCSI_DEV *Dev;
+ EFI_STATUS Status;
+
+ Dev = (VSCSI_DEV *) AllocateZeroPool (sizeof *Dev);
+ if (Dev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ (VOID **)&Dev->PciIo, 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.
+ //
+ Status = VirtioScsiInit (Dev);
+ if (EFI_ERROR (Status)) {
+ goto RestorePciAttributes;
+ }
+
+ //
+ // Setup complete, attempt to export the driver instance's PassThru
+ // interface.
+ //
+ Dev->Signature = VSCSI_SIG;
+ Status = gBS->InstallProtocolInterface (&DeviceHandle,
+ &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,
+ &Dev->PassThru);
+ if (EFI_ERROR (Status)) {
+ goto UninitDev;
+ }
+
+ return EFI_SUCCESS;
+
+UninitDev:
+ VirtioScsiUninit (Dev);
+
+RestorePciAttributes:
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
+ Dev->OriginalPciAttributes, NULL);
+
+ClosePciIo:
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+FreeVirtioScsi:
+ FreePool (Dev);
+
+ return Status;
+}
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+ VSCSI_DEV *Dev;
+
+ Status = gBS->OpenProtocol (
+ DeviceHandle, // candidate device
+ &gEfiExtScsiPassThruProtocolGuid, // retrieve the SCSI iface
+ (VOID **)&PassThru, // target pointer
+ This->DriverBindingHandle, // requestor driver ident.
+ DeviceHandle, // lookup req. for dev.
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Dev = VIRTIO_SCSI_FROM_PASS_THRU (PassThru);
+
+ //
+ // Handle Stop() requests for in-use driver instances gracefully.
+ //
+ Status = gBS->UninstallProtocolInterface (DeviceHandle,
+ &gEfiExtScsiPassThruProtocolGuid, &Dev->PassThru);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ VirtioScsiUninit (Dev);
+
+ Dev->PciIo->Attributes (Dev->PciIo, EfiPciIoAttributeOperationSet,
+ Dev->OriginalPciAttributes, NULL);
+
+ gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle, DeviceHandle);
+
+ FreePool (Dev);
+
+ return EFI_SUCCESS;
+}
+
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+ &VirtioScsiDriverBindingSupported,
+ &VirtioScsiDriverBindingStart,
+ &VirtioScsiDriverBindingStop,
+ 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+ NULL, // ImageHandle, to be overwritten by
+ // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
+ NULL // DriverBindingHandle, ditto
+};
+
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+// Device type names ("Virtio SCSI Host Device") are not formatted because the
+// driver supports only that device type. Therefore the driver name suffices
+// for unambiguous identification.
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+ { "eng;en", L"Virtio SCSI Host Driver" },
+ { NULL, NULL }
+};
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gComponentName) // Iso639Language
+ );
+}
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetDeviceName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+STATIC
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+ &VirtioScsiGetDriverName,
+ &VirtioScsiGetDeviceName,
+ "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+STATIC
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioScsiGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioScsiGetDeviceName,
+ "en" // SupportedLanguages, RFC 4646 language codes
+};
+
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+VirtioScsiEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gDriverBinding,
+ ImageHandle,
+ &gComponentName,
+ &gComponentName2
+ );
+}
diff --git a/OvmfPkg/VirtioScsiDxe/VirtioScsi.h b/OvmfPkg/VirtioScsiDxe/VirtioScsi.h
new file mode 100644
index 0000000000..f5220b2215
--- /dev/null
+++ b/OvmfPkg/VirtioScsiDxe/VirtioScsi.h
@@ -0,0 +1,209 @@
+/** @file
+
+ Internal definitions for the virtio-scsi driver, which produces Extended SCSI
+ Pass Thru Protocol instances for virtio-scsi devices.
+
+ Copyright (C) 2012, Red Hat, Inc.
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _VIRTIO_SCSI_DXE_H_
+#define _VIRTIO_SCSI_DXE_H_
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/ScsiPassThruExt.h>
+
+#include <IndustryStandard/Virtio.h>
+
+
+//
+// This driver supports 2-byte target identifiers and 4-byte LUN identifiers.
+//
+// EFI_EXT_SCSI_PASS_THRU_PROTOCOL provides TARGET_MAX_BYTES bytes for target
+// identification, and 8 bytes for LUN identification.
+//
+// EFI_EXT_SCSI_PASS_THRU_MODE.AdapterId is also a target identifier,
+// consisting of 4 bytes. Make sure TARGET_MAX_BYTES can accomodate both
+// AdapterId and our target identifiers.
+//
+#if TARGET_MAX_BYTES < 4
+# error "virtio-scsi requires TARGET_MAX_BYTES >= 4"
+#endif
+
+
+#define VSCSI_SIG SIGNATURE_32 ('V', 'S', 'C', 'S')
+
+typedef struct {
+ //
+ // Parts of this structure are initialized / torn down in various functions
+ // at various call depths. The table to the right should make it easier to
+ // track them.
+ //
+ // field init function init depth
+ // ---------------------- ------------------ ----------
+ UINT32 Signature; // DriverBindingStart 0
+ EFI_PCI_IO_PROTOCOL *PciIo; // DriverBindingStart 0
+ UINT64 OriginalPciAttributes; // DriverBindingStart 0
+ BOOLEAN InOutSupported; // VirtioScsiInit 1
+ UINT16 MaxTarget; // VirtioScsiInit 1
+ UINT32 MaxLun; // VirtioScsiInit 1
+ UINT32 MaxSectors; // VirtioScsiInit 1
+ VRING Ring; // VirtioRingInit 2
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; // VirtioScsiInit 1
+ EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode; // VirtioScsiInit 1
+} VSCSI_DEV;
+
+#define VIRTIO_SCSI_FROM_PASS_THRU(PassThruPointer) \
+ CR (PassThruPointer, VSCSI_DEV, PassThru, VSCSI_SIG)
+
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+
+EFI_STATUS
+EFIAPI
+VirtioScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+
+//
+// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
+// - 14.1 SCSI Driver Model Overview,
+// - 14.7 Extended SCSI Pass Thru Protocol.
+//
+
+EFI_STATUS
+EFIAPI
+VirtioScsiPassThru (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ );
+
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+// Device type names ("Virtio SCSI Host Device") are not formatted because the
+// driver supports only that device type. Therefore the driver name suffices
+// for unambiguous identification.
+//
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+EFI_STATUS
+EFIAPI
+VirtioScsiGetDeviceName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN EFI_HANDLE ChildHandle,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+#endif // _VIRTIO_SCSI_DXE_H_
diff --git a/OvmfPkg/VirtioScsiDxe/VirtioScsi.inf b/OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
new file mode 100644
index 0000000000..8209c50275
--- /dev/null
+++ b/OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
@@ -0,0 +1,47 @@
+## @file
+# This driver produces Extended SCSI Pass Thru Protocol instances for
+# virtio-scsi devices.
+#
+# Copyright (C) 2012, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License which accompanies this
+# distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VirtioScsiDxe
+ FILE_GUID = FAB5D4F4-83C0-4AAF-8480-442D11DF6CEA
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = VirtioScsiEntryPoint
+
+[Sources]
+ VirtioScsi.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ VirtioLib
+
+[Protocols]
+ gEfiExtScsiPassThruProtocolGuid ## BY_START
+ gEfiPciIoProtocolGuid ## TO_START
+
+[Pcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxTargetLimit ## CONSUMES
+ gUefiOvmfPkgTokenSpaceGuid.PcdVirtioScsiMaxLunLimit ## CONSUMES