]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: introduce virtio-scsi driver
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Thu, 18 Oct 2012 17:07:48 +0000 (17:07 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Thu, 18 Oct 2012 17:07:48 +0000 (17:07 +0000)
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

OvmfPkg/Include/IndustryStandard/VirtioScsi.h [new file with mode: 0644]
OvmfPkg/OvmfPkg.dec
OvmfPkg/OvmfPkgIa32.dsc
OvmfPkg/OvmfPkgIa32.fdf
OvmfPkg/OvmfPkgIa32X64.dsc
OvmfPkg/OvmfPkgIa32X64.fdf
OvmfPkg/OvmfPkgX64.dsc
OvmfPkg/OvmfPkgX64.fdf
OvmfPkg/VirtioScsiDxe/VirtioScsi.c [new file with mode: 0644]
OvmfPkg/VirtioScsiDxe/VirtioScsi.h [new file with mode: 0644]
OvmfPkg/VirtioScsiDxe/VirtioScsi.inf [new file with mode: 0644]

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