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