--- /dev/null
+/** @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