summaryrefslogtreecommitdiff
path: root/OvmfPkg/VirtioScsiDxe/VirtioScsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/VirtioScsiDxe/VirtioScsi.c')
-rw-r--r--OvmfPkg/VirtioScsiDxe/VirtioScsi.c1228
1 files changed, 0 insertions, 1228 deletions
diff --git a/OvmfPkg/VirtioScsiDxe/VirtioScsi.c b/OvmfPkg/VirtioScsiDxe/VirtioScsi.c
deleted file mode 100644
index 2cb3f43bb0..0000000000
--- a/OvmfPkg/VirtioScsiDxe/VirtioScsi.c
+++ /dev/null
@@ -1,1228 +0,0 @@
-/** @file
-
- This driver produces Extended SCSI Pass Thru Protocol instances for
- virtio-scsi devices.
-
- The implementation is basic:
-
- - No hotplug / hot-unplug.
-
- - Although EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() could be a good match
- for multiple in-flight virtio-scsi requests, we stick to synchronous
- requests for now.
-
- - Timeouts are not supported for EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru().
-
- - Only one channel is supported. (At the time of this writing, host-side
- virtio-scsi supports a single channel too.)
-
- - Only one request queue is used (for the one synchronous request).
-
- - The ResetChannel() and ResetTargetLun() functions of
- EFI_EXT_SCSI_PASS_THRU_PROTOCOL are not supported (which is allowed by the
- UEFI 2.3.1 Errata C specification), although
- VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET could be a good match. That would
- however require client code for the control queue, which is deemed
- unreasonable for now.
-
- Copyright (C) 2012, Red Hat, Inc.
- Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>
-
- This program and the accompanying materials are licensed and made available
- under the terms and conditions of the BSD License which accompanies this
- distribution. The full text of the license may be found at
- http://opensource.org/licenses/bsd-license.php
-
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-**/
-
-#include <IndustryStandard/VirtioScsi.h>
-#include <Library/BaseMemoryLib.h>
-#include <Library/DebugLib.h>
-#include <Library/MemoryAllocationLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-#include <Library/UefiLib.h>
-#include <Library/VirtioLib.h>
-
-#include "VirtioScsi.h"
-
-/**
-
- Convenience macros to read and write configuration elements of the
- virtio-scsi VirtIo device.
-
- The following macros make it possible to specify only the "core parameters"
- for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()
- returns, the transaction will have been completed.
-
- @param[in] Dev Pointer to the VSCSI_DEV structure.
-
- @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
- configuration item to access.
-
- @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the
- selected configuration item.
-
- @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the
- value read from the configuration item. Its type must be
- one of UINT8, UINT16, UINT32, UINT64.
-
-
- @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice().
-
-**/
-
-#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \
- (Dev)->VirtIo, \
- OFFSET_OF_VSCSI (Field), \
- SIZE_OF_VSCSI (Field), \
- (Value) \
- ))
-
-#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \
- (Dev)->VirtIo, \
- OFFSET_OF_VSCSI (Field), \
- SIZE_OF_VSCSI (Field), \
- sizeof *(Pointer), \
- (Pointer) \
- ))
-
-
-//
-// UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
-// the PassThru() interface. Beside returning a status code, the function must
-// set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
-// parameter on return. The following is a full list of those fields, for
-// easier validation of PopulateRequest(), ParseResponse(), and
-// VirtioScsiPassThru() below.
-//
-// - InTransferLength
-// - OutTransferLength
-// - HostAdapterStatus
-// - TargetStatus
-// - SenseDataLength
-// - SenseData
-//
-// On any return from the PassThru() interface, these fields must be set,
-// except if the returned status code is explicitly exempt. (Actually the
-// implementation here conservatively sets these fields even in case not all
-// of them would be required by the specification.)
-//
-
-/**
-
- Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
- packet.
-
- The caller is responsible for pre-zeroing the virtio-scsi request. The
- Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
- by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
-
- @param[in] Dev The virtio-scsi host device the packet targets.
-
- @param[in] Target The SCSI target controlled by the virtio-scsi host
- device.
-
- @param[in] Lun The Logical Unit Number under the SCSI target.
-
- @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
- function translates to a virtio-scsi request. On
- failure this parameter relays error contents.
-
- @param[out] Request The pre-zeroed virtio-scsi request to populate. This
- parameter is volatile-qualified because we expect the
- caller to append it to a virtio ring, thus
- assignments to Request must be visible when the
- function returns.
-
-
- @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
- Request has been populated.
-
- @return Otherwise, invalid or unsupported parameters were
- detected. Status codes are meant for direct forwarding
- by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
- implementation.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-PopulateRequest (
- IN CONST VSCSI_DEV *Dev,
- IN UINT16 Target,
- IN UINT64 Lun,
- IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
- OUT volatile VIRTIO_SCSI_REQ *Request
- )
-{
- UINTN Idx;
-
- if (
- //
- // bidirectional transfer was requested, but the host doesn't support it
- //
- (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0 &&
- !Dev->InOutSupported) ||
-
- //
- // a target / LUN was addressed that's impossible to encode for the host
- //
- Target > 0xFF || Lun >= 0x4000 ||
-
- //
- // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
- //
- Packet->CdbLength > VIRTIO_SCSI_CDB_SIZE ||
-
- //
- // From virtio-0.9.5, 2.3.2 Descriptor Table:
- // "no descriptor chain may be more than 2^32 bytes long in total".
- //
- (UINT64) Packet->InTransferLength + Packet->OutTransferLength > SIZE_1GB
- ) {
-
- //
- // this error code doesn't require updates to the Packet output fields
- //
- return EFI_UNSUPPORTED;
- }
-
- if (
- //
- // addressed invalid device
- //
- Target > Dev->MaxTarget || Lun > Dev->MaxLun ||
-
- //
- // invalid direction (there doesn't seem to be a macro for the "no data
- // transferred" "direction", eg. for TEST UNIT READY)
- //
- Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
-
- //
- // trying to receive, but destination pointer is NULL, or contradicting
- // transfer direction
- //
- (Packet->InTransferLength > 0 &&
- (Packet->InDataBuffer == NULL ||
- Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
- )
- ) ||
-
- //
- // trying to send, but source pointer is NULL, or contradicting transfer
- // direction
- //
- (Packet->OutTransferLength > 0 &&
- (Packet->OutDataBuffer == NULL ||
- Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
- )
- )
- ) {
-
- //
- // this error code doesn't require updates to the Packet output fields
- //
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // Catch oversized requests eagerly. If this condition evaluates to false,
- // then the combined size of a bidirectional request will not exceed the
- // virtio-scsi device's transfer limit either.
- //
- if (ALIGN_VALUE (Packet->OutTransferLength, 512) / 512
- > Dev->MaxSectors / 2 ||
- ALIGN_VALUE (Packet->InTransferLength, 512) / 512
- > Dev->MaxSectors / 2) {
- Packet->InTransferLength = (Dev->MaxSectors / 2) * 512;
- Packet->OutTransferLength = (Dev->MaxSectors / 2) * 512;
- Packet->HostAdapterStatus =
- EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
- Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
- Packet->SenseDataLength = 0;
- return EFI_BAD_BUFFER_SIZE;
- }
-
- //
- // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,
- // Device Operation: request queues
- //
- Request->Lun[0] = 1;
- Request->Lun[1] = (UINT8) Target;
- Request->Lun[2] = (UINT8) (((UINT32)Lun >> 8) | 0x40);
- Request->Lun[3] = (UINT8) Lun;
-
- //
- // CopyMem() would cast away the "volatile" qualifier before access, which is
- // undefined behavior (ISO C99 6.7.3p5)
- //
- for (Idx = 0; Idx < Packet->CdbLength; ++Idx) {
- Request->Cdb[Idx] = ((UINT8 *) Packet->Cdb)[Idx];
- }
-
- return EFI_SUCCESS;
-}
-
-
-/**
-
- Parse the virtio-scsi device's response, translate it to an EFI status code,
- and update the Extended SCSI Pass Thru Protocol packet, to be returned by
- the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
-
- @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
- been translated to a virtio-scsi request with
- PopulateRequest(), and processed by the host. On
- output this parameter is updated with response or
- error contents.
-
- @param[in] Response The virtio-scsi response structure to parse. We expect
- it to come from a virtio ring, thus it is qualified
- volatile.
-
-
- @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
- Extended SCSI Pass Thru Protocol.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-ParseResponse (
- IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
- IN CONST volatile VIRTIO_SCSI_RESP *Response
- )
-{
- UINTN ResponseSenseLen;
- UINTN Idx;
-
- //
- // return sense data (length and contents) in all cases, truncated if needed
- //
- ResponseSenseLen = MIN (Response->SenseLen, VIRTIO_SCSI_SENSE_SIZE);
- if (Packet->SenseDataLength > ResponseSenseLen) {
- Packet->SenseDataLength = (UINT8) ResponseSenseLen;
- }
- for (Idx = 0; Idx < Packet->SenseDataLength; ++Idx) {
- ((UINT8 *) Packet->SenseData)[Idx] = Response->Sense[Idx];
- }
-
- //
- // Report actual transfer lengths. The logic below covers all three
- // DataDirections (read, write, bidirectional).
- //
- // -+- @ 0
- // |
- // | write ^ @ Residual (unprocessed)
- // | |
- // -+- @ OutTransferLength -+- @ InTransferLength
- // | |
- // | read |
- // | |
- // V @ OutTransferLength + InTransferLength -+- @ 0
- //
- if (Response->Residual <= Packet->InTransferLength) {
- Packet->InTransferLength -= Response->Residual;
- }
- else {
- Packet->OutTransferLength -= Response->Residual - Packet->InTransferLength;
- Packet->InTransferLength = 0;
- }
-
- //
- // report target status in all cases
- //
- Packet->TargetStatus = Response->Status;
-
- //
- // host adapter status and function return value depend on virtio-scsi
- // response code
- //
- switch (Response->Response) {
- case VIRTIO_SCSI_S_OK:
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
- return EFI_SUCCESS;
-
- case VIRTIO_SCSI_S_OVERRUN:
- Packet->HostAdapterStatus =
- EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
- break;
-
- case VIRTIO_SCSI_S_BAD_TARGET:
- //
- // This is non-intuitive but explicitly required by the
- // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
- // disconnected (but otherwise valid) target / LUN addresses.
- //
- Packet->HostAdapterStatus =
- EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;
- return EFI_TIMEOUT;
-
- case VIRTIO_SCSI_S_RESET:
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;
- break;
-
- case VIRTIO_SCSI_S_BUSY:
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
- return EFI_NOT_READY;
-
- //
- // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
- // intentional as well, not an oversight.
- //
- case VIRTIO_SCSI_S_ABORTED:
- case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
- case VIRTIO_SCSI_S_TARGET_FAILURE:
- case VIRTIO_SCSI_S_NEXUS_FAILURE:
- case VIRTIO_SCSI_S_FAILURE:
- default:
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
- }
-
- return EFI_DEVICE_ERROR;
-}
-
-
-//
-// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
-// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
-// - 14.1 SCSI Driver Model Overview,
-// - 14.7 Extended SCSI Pass Thru Protocol.
-//
-
-EFI_STATUS
-EFIAPI
-VirtioScsiPassThru (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN UINT8 *Target,
- IN UINT64 Lun,
- IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
- IN EFI_EVENT Event OPTIONAL
- )
-{
- VSCSI_DEV *Dev;
- UINT16 TargetValue;
- EFI_STATUS Status;
- volatile VIRTIO_SCSI_REQ Request;
- volatile VIRTIO_SCSI_RESP Response;
- DESC_INDICES Indices;
-
- ZeroMem ((VOID*) &Request, sizeof (Request));
- ZeroMem ((VOID*) &Response, sizeof (Response));
-
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
- CopyMem (&TargetValue, Target, sizeof TargetValue);
-
- Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- VirtioPrepare (&Dev->Ring, &Indices);
-
- //
- // preset a host status for ourselves that we do not accept as success
- //
- Response.Response = VIRTIO_SCSI_S_FAILURE;
-
- //
- // ensured by VirtioScsiInit() -- this predicate, in combination with the
- // lock-step progress, ensures we don't have to track free descriptors.
- //
- ASSERT (Dev->Ring.QueueSize >= 4);
-
- //
- // enqueue Request
- //
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
- VRING_DESC_F_NEXT, &Indices);
-
- //
- // enqueue "dataout" if any
- //
- if (Packet->OutTransferLength > 0) {
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->OutDataBuffer,
- Packet->OutTransferLength, VRING_DESC_F_NEXT, &Indices);
- }
-
- //
- // enqueue Response, to be written by the host
- //
- VirtioAppendDesc (&Dev->Ring, (UINTN) &Response, sizeof Response,
- VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ?
- VRING_DESC_F_NEXT : 0),
- &Indices);
-
- //
- // enqueue "datain" if any, to be written by the host
- //
- if (Packet->InTransferLength > 0) {
- VirtioAppendDesc (&Dev->Ring, (UINTN) Packet->InDataBuffer,
- Packet->InTransferLength, VRING_DESC_F_WRITE, &Indices);
- }
-
- // If kicking the host fails, we must fake a host adapter error.
- // EFI_NOT_READY would save us the effort, but it would also suggest that the
- // caller retry.
- //
- if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,
- &Indices) != EFI_SUCCESS) {
- Packet->InTransferLength = 0;
- Packet->OutTransferLength = 0;
- Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
- Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
- Packet->SenseDataLength = 0;
- return EFI_DEVICE_ERROR;
- }
-
- return ParseResponse (Packet, &Response);
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiGetNextTargetLun (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN OUT UINT8 **TargetPointer,
- IN OUT UINT64 *Lun
- )
-{
- UINT8 *Target;
- UINTN Idx;
- UINT16 LastTarget;
- VSCSI_DEV *Dev;
-
- //
- // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
- //
- Target = *TargetPointer;
-
- //
- // Search for first non-0xFF byte. If not found, return first target & LUN.
- //
- for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
- ;
- if (Idx == TARGET_MAX_BYTES) {
- SetMem (Target, TARGET_MAX_BYTES, 0x00);
- *Lun = 0;
- return EFI_SUCCESS;
- }
-
- //
- // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
- //
- CopyMem (&LastTarget, Target, sizeof LastTarget);
-
- //
- // increment (target, LUN) pair if valid on input
- //
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
- if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (*Lun < Dev->MaxLun) {
- ++*Lun;
- return EFI_SUCCESS;
- }
-
- if (LastTarget < Dev->MaxTarget) {
- *Lun = 0;
- ++LastTarget;
- CopyMem (Target, &LastTarget, sizeof LastTarget);
- return EFI_SUCCESS;
- }
-
- return EFI_NOT_FOUND;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiBuildDevicePath (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN UINT8 *Target,
- IN UINT64 Lun,
- IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
- )
-{
- UINT16 TargetValue;
- VSCSI_DEV *Dev;
- SCSI_DEVICE_PATH *ScsiDevicePath;
-
- if (DevicePath == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- CopyMem (&TargetValue, Target, sizeof TargetValue);
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
- if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) {
- return EFI_NOT_FOUND;
- }
-
- ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);
- if (ScsiDevicePath == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
- ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
- ScsiDevicePath->Header.Length[0] = (UINT8) sizeof *ScsiDevicePath;
- ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8);
- ScsiDevicePath->Pun = TargetValue;
- ScsiDevicePath->Lun = (UINT16) Lun;
-
- *DevicePath = &ScsiDevicePath->Header;
- return EFI_SUCCESS;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiGetTargetLun (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT UINT8 **TargetPointer,
- OUT UINT64 *Lun
- )
-{
- SCSI_DEVICE_PATH *ScsiDevicePath;
- VSCSI_DEV *Dev;
- UINT8 *Target;
-
- if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL ||
- Lun == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
- DevicePath->SubType != MSG_SCSI_DP) {
- return EFI_UNSUPPORTED;
- }
-
- ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath;
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
- if (ScsiDevicePath->Pun > Dev->MaxTarget ||
- ScsiDevicePath->Lun > Dev->MaxLun) {
- return EFI_NOT_FOUND;
- }
-
- //
- // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
- // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
- // c) ScsiDevicePath->Pun is an UINT16
- //
- Target = *TargetPointer;
- CopyMem (Target, &ScsiDevicePath->Pun, 2);
- SetMem (Target + 2, TARGET_MAX_BYTES - 2, 0x00);
-
- *Lun = ScsiDevicePath->Lun;
- return EFI_SUCCESS;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiResetChannel (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
- )
-{
- return EFI_UNSUPPORTED;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiResetTargetLun (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN UINT8 *Target,
- IN UINT64 Lun
- )
-{
- return EFI_UNSUPPORTED;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiGetNextTarget (
- IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
- IN OUT UINT8 **TargetPointer
- )
-{
- UINT8 *Target;
- UINTN Idx;
- UINT16 LastTarget;
- VSCSI_DEV *Dev;
-
- //
- // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
- //
- Target = *TargetPointer;
-
- //
- // Search for first non-0xFF byte. If not found, return first target.
- //
- for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
- ;
- if (Idx == TARGET_MAX_BYTES) {
- SetMem (Target, TARGET_MAX_BYTES, 0x00);
- return EFI_SUCCESS;
- }
-
- //
- // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
- //
- CopyMem (&LastTarget, Target, sizeof LastTarget);
-
- //
- // increment target if valid on input
- //
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
- if (LastTarget > Dev->MaxTarget) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (LastTarget < Dev->MaxTarget) {
- ++LastTarget;
- CopyMem (Target, &LastTarget, sizeof LastTarget);
- return EFI_SUCCESS;
- }
-
- return EFI_NOT_FOUND;
-}
-
-
-STATIC
-EFI_STATUS
-EFIAPI
-VirtioScsiInit (
- IN OUT VSCSI_DEV *Dev
- )
-{
- UINT8 NextDevStat;
- EFI_STATUS Status;
-
- UINT32 Features;
- UINT16 MaxChannel; // for validation only
- UINT32 NumQueues; // for validation only
- UINT16 QueueSize;
-
- //
- // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
- //
- NextDevStat = 0; // step 1 -- reset device
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
-
- NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
-
- NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
-
- //
- // Set Page Size - MMIO VirtIo Specific
- //
- Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
-
- //
- // step 4a -- retrieve and validate features
- //
- Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- Dev->InOutSupported = (BOOLEAN) ((Features & VIRTIO_SCSI_F_INOUT) != 0);
-
- Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (MaxChannel != 0) {
- //
- // this driver is for a single-channel virtio-scsi HBA
- //
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
-
- Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (NumQueues < 1) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
-
- Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (Dev->MaxTarget > PcdGet16 (PcdVirtioScsiMaxTargetLimit)) {
- Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);
- }
-
- Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (Dev->MaxLun > PcdGet32 (PcdVirtioScsiMaxLunLimit)) {
- Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);
- }
-
- Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (Dev->MaxSectors < 2) {
- //
- // We must be able to halve it for bidirectional transfers
- // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
- //
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
-
- //
- // step 4b -- allocate request virtqueue
- //
- Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // VirtioScsiPassThru() uses at most four descriptors
- //
- if (QueueSize < 4) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
-
- Status = VirtioRingInit (QueueSize, &Dev->Ring);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
-
- //
- // Additional steps for MMIO: align the queue appropriately, and set the
- // size. If anything fails from here on, we must release the ring resources.
- //
- Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- //
- // step 4c -- Report GPFN (guest-physical frame number) of queue.
- //
- Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo,
- (UINT32) ((UINTN) Dev->Ring.Base >> EFI_PAGE_SHIFT));
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- //
- // step 5 -- Report understood features and guest-tuneables. We want none of
- // the known (or unknown) VIRTIO_SCSI_F_* or VIRTIO_F_* capabilities (see
- // virtio-0.9.5, Appendices B and I), except bidirectional transfers.
- //
- Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo,
- Features & VIRTIO_SCSI_F_INOUT);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- //
- // We expect these maximum sizes from the host. Since they are
- // guest-negotiable, ask for them rather than just checking them.
- //
- Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
- Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- //
- // step 6 -- initialization complete
- //
- NextDevStat |= VSTAT_DRIVER_OK;
- Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
-
- //
- // populate the exported interface's attributes
- //
- Dev->PassThru.Mode = &Dev->PassThruMode;
- Dev->PassThru.PassThru = &VirtioScsiPassThru;
- Dev->PassThru.GetNextTargetLun = &VirtioScsiGetNextTargetLun;
- Dev->PassThru.BuildDevicePath = &VirtioScsiBuildDevicePath;
- Dev->PassThru.GetTargetLun = &VirtioScsiGetTargetLun;
- Dev->PassThru.ResetChannel = &VirtioScsiResetChannel;
- Dev->PassThru.ResetTargetLun = &VirtioScsiResetTargetLun;
- Dev->PassThru.GetNextTarget = &VirtioScsiGetNextTarget;
-
- //
- // AdapterId is a target for which no handle will be created during bus scan.
- // Prevent any conflict with real devices.
- //
- Dev->PassThruMode.AdapterId = 0xFFFFFFFF;
-
- //
- // Set both physical and logical attributes for non-RAID SCSI channel. See
- // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
- // SCSI Pass Thru Protocol.
- //
- Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
- EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
-
- //
- // no restriction on transfer buffer alignment
- //
- Dev->PassThruMode.IoAlign = 0;
-
- return EFI_SUCCESS;
-
-ReleaseQueue:
- VirtioRingUninit (&Dev->Ring);
-
-Failed:
- //
- // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
- // Status. VirtIo access failure here should not mask the original error.
- //
- NextDevStat |= VSTAT_FAILED;
- Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
-
- Dev->InOutSupported = FALSE;
- Dev->MaxTarget = 0;
- Dev->MaxLun = 0;
- Dev->MaxSectors = 0;
-
- return Status; // reached only via Failed above
-}
-
-
-
-STATIC
-VOID
-EFIAPI
-VirtioScsiUninit (
- IN OUT VSCSI_DEV *Dev
- )
-{
- //
- // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
- // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
- // the old comms area.
- //
- Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
-
- Dev->InOutSupported = FALSE;
- Dev->MaxTarget = 0;
- Dev->MaxLun = 0;
- Dev->MaxSectors = 0;
-
- VirtioRingUninit (&Dev->Ring);
-
- SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);
- SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);
-}
-
-
-//
-// Probe, start and stop functions of this driver, called by the DXE core for
-// specific devices.
-//
-// The following specifications document these interfaces:
-// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
-// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
-//
-// The implementation follows:
-// - Driver Writer's Guide for UEFI 2.3.1 v1.01
-// - 5.1.3.4 OpenProtocol() and CloseProtocol()
-// - UEFI Spec 2.3.1 + Errata C
-// - 6.3 Protocol Handler Services
-//
-
-EFI_STATUS
-EFIAPI
-VirtioScsiDriverBindingSupported (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
- )
-{
- EFI_STATUS Status;
- VIRTIO_DEVICE_PROTOCOL *VirtIo;
-
- //
- // Attempt to open the device with the VirtIo set of interfaces. On success,
- // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
- // attempts (EFI_ALREADY_STARTED).
- //
- Status = gBS->OpenProtocol (
- DeviceHandle, // candidate device
- &gVirtioDeviceProtocolGuid, // for generic VirtIo access
- (VOID **)&VirtIo, // handle to instantiate
- This->DriverBindingHandle, // requestor driver identity
- DeviceHandle, // ControllerHandle, according to
- // the UEFI Driver Model
- EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
- // the device; to be released
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {
- Status = EFI_UNSUPPORTED;
- }
-
- //
- // We needed VirtIo access only transitorily, to see whether we support the
- // device or not.
- //
- gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
- This->DriverBindingHandle, DeviceHandle);
- return Status;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiDriverBindingStart (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
- )
-{
- VSCSI_DEV *Dev;
- EFI_STATUS Status;
-
- Dev = (VSCSI_DEV *) AllocateZeroPool (sizeof *Dev);
- if (Dev == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
- (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
- DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
- if (EFI_ERROR (Status)) {
- goto FreeVirtioScsi;
- }
-
- //
- // VirtIo access granted, configure virtio-scsi device.
- //
- Status = VirtioScsiInit (Dev);
- if (EFI_ERROR (Status)) {
- goto CloseVirtIo;
- }
-
- //
- // Setup complete, attempt to export the driver instance's PassThru
- // interface.
- //
- Dev->Signature = VSCSI_SIG;
- Status = gBS->InstallProtocolInterface (&DeviceHandle,
- &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,
- &Dev->PassThru);
- if (EFI_ERROR (Status)) {
- goto UninitDev;
- }
-
- return EFI_SUCCESS;
-
-UninitDev:
- VirtioScsiUninit (Dev);
-
-CloseVirtIo:
- gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
- This->DriverBindingHandle, DeviceHandle);
-
-FreeVirtioScsi:
- FreePool (Dev);
-
- return Status;
-}
-
-
-EFI_STATUS
-EFIAPI
-VirtioScsiDriverBindingStop (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN UINTN NumberOfChildren,
- IN EFI_HANDLE *ChildHandleBuffer
- )
-{
- EFI_STATUS Status;
- EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
- VSCSI_DEV *Dev;
-
- Status = gBS->OpenProtocol (
- DeviceHandle, // candidate device
- &gEfiExtScsiPassThruProtocolGuid, // retrieve the SCSI iface
- (VOID **)&PassThru, // target pointer
- This->DriverBindingHandle, // requestor driver ident.
- DeviceHandle, // lookup req. for dev.
- EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- Dev = VIRTIO_SCSI_FROM_PASS_THRU (PassThru);
-
- //
- // Handle Stop() requests for in-use driver instances gracefully.
- //
- Status = gBS->UninstallProtocolInterface (DeviceHandle,
- &gEfiExtScsiPassThruProtocolGuid, &Dev->PassThru);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- VirtioScsiUninit (Dev);
-
- gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
- This->DriverBindingHandle, DeviceHandle);
-
- FreePool (Dev);
-
- return EFI_SUCCESS;
-}
-
-
-//
-// The static object that groups the Supported() (ie. probe), Start() and
-// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
-// C, 10.1 EFI Driver Binding Protocol.
-//
-STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
- &VirtioScsiDriverBindingSupported,
- &VirtioScsiDriverBindingStart,
- &VirtioScsiDriverBindingStop,
- 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
- NULL, // ImageHandle, to be overwritten by
- // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
- NULL // DriverBindingHandle, ditto
-};
-
-
-//
-// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
-// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
-// in English, for display on standard console devices. This is recommended for
-// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
-// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
-//
-// Device type names ("Virtio SCSI Host Device") are not formatted because the
-// driver supports only that device type. Therefore the driver name suffices
-// for unambiguous identification.
-//
-
-STATIC
-EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
- { "eng;en", L"Virtio SCSI Host Driver" },
- { NULL, NULL }
-};
-
-STATIC
-EFI_COMPONENT_NAME_PROTOCOL gComponentName;
-
-EFI_STATUS
-EFIAPI
-VirtioScsiGetDriverName (
- IN EFI_COMPONENT_NAME_PROTOCOL *This,
- IN CHAR8 *Language,
- OUT CHAR16 **DriverName
- )
-{
- return LookupUnicodeString2 (
- Language,
- This->SupportedLanguages,
- mDriverNameTable,
- DriverName,
- (BOOLEAN)(This == &gComponentName) // Iso639Language
- );
-}
-
-EFI_STATUS
-EFIAPI
-VirtioScsiGetDeviceName (
- IN EFI_COMPONENT_NAME_PROTOCOL *This,
- IN EFI_HANDLE DeviceHandle,
- IN EFI_HANDLE ChildHandle,
- IN CHAR8 *Language,
- OUT CHAR16 **ControllerName
- )
-{
- return EFI_UNSUPPORTED;
-}
-
-STATIC
-EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
- &VirtioScsiGetDriverName,
- &VirtioScsiGetDeviceName,
- "eng" // SupportedLanguages, ISO 639-2 language codes
-};
-
-STATIC
-EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
- (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioScsiGetDriverName,
- (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioScsiGetDeviceName,
- "en" // SupportedLanguages, RFC 4646 language codes
-};
-
-
-//
-// Entry point of this driver.
-//
-EFI_STATUS
-EFIAPI
-VirtioScsiEntryPoint (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- return EfiLibInstallDriverBindingComponentName2 (
- ImageHandle,
- SystemTable,
- &gDriverBinding,
- ImageHandle,
- &gComponentName,
- &gComponentName2
- );
-}