3 This driver produces Extended SCSI Pass Thru Protocol instances for
6 The implementation is basic:
8 - No hotplug / hot-unplug.
10 - Although EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() could be a good match
11 for multiple in-flight virtio-scsi requests, we stick to synchronous
14 - Timeouts are not supported for EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru().
16 - Only one channel is supported. (At the time of this writing, host-side
17 virtio-scsi supports a single channel too.)
19 - Only one request queue is used (for the one synchronous request).
21 - The ResetChannel() and ResetTargetLun() functions of
22 EFI_EXT_SCSI_PASS_THRU_PROTOCOL are not supported (which is allowed by the
23 UEFI 2.3.1 Errata C specification), although
24 VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET could be a good match. That would
25 however require client code for the control queue, which is deemed
28 Copyright (C) 2012, Red Hat, Inc.
29 Copyright (c) 2012 - 2014, Intel Corporation. All rights reserved.<BR>
31 This program and the accompanying materials are licensed and made available
32 under the terms and conditions of the BSD License which accompanies this
33 distribution. The full text of the license may be found at
34 http://opensource.org/licenses/bsd-license.php
36 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
37 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
41 #include <IndustryStandard/VirtioScsi.h>
42 #include <Library/BaseMemoryLib.h>
43 #include <Library/DebugLib.h>
44 #include <Library/MemoryAllocationLib.h>
45 #include <Library/UefiBootServicesTableLib.h>
46 #include <Library/UefiLib.h>
47 #include <Library/VirtioLib.h>
49 #include "VirtioScsi.h"
53 Convenience macros to read and write configuration elements of the
54 virtio-scsi VirtIo device.
56 The following macros make it possible to specify only the "core parameters"
57 for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()
58 returns, the transaction will have been completed.
60 @param[in] Dev Pointer to the VSCSI_DEV structure.
62 @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
63 configuration item to access.
65 @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the
66 selected configuration item.
68 @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the
69 value read from the configuration item. Its type must be
70 one of UINT8, UINT16, UINT32, UINT64.
73 @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice().
77 #define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \
79 OFFSET_OF_VSCSI (Field), \
80 SIZE_OF_VSCSI (Field), \
84 #define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \
86 OFFSET_OF_VSCSI (Field), \
87 SIZE_OF_VSCSI (Field), \
94 // UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
95 // the PassThru() interface. Beside returning a status code, the function must
96 // set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
97 // parameter on return. The following is a full list of those fields, for
98 // easier validation of PopulateRequest(), ParseResponse(), and
99 // VirtioScsiPassThru() below.
101 // - InTransferLength
102 // - OutTransferLength
103 // - HostAdapterStatus
108 // On any return from the PassThru() interface, these fields must be set,
109 // except if the returned status code is explicitly exempt. (Actually the
110 // implementation here conservatively sets these fields even in case not all
111 // of them would be required by the specification.)
116 Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
119 The caller is responsible for pre-zeroing the virtio-scsi request. The
120 Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
121 by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
123 @param[in] Dev The virtio-scsi host device the packet targets.
125 @param[in] Target The SCSI target controlled by the virtio-scsi host
128 @param[in] Lun The Logical Unit Number under the SCSI target.
130 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
131 function translates to a virtio-scsi request. On
132 failure this parameter relays error contents.
134 @param[out] Request The pre-zeroed virtio-scsi request to populate. This
135 parameter is volatile-qualified because we expect the
136 caller to append it to a virtio ring, thus
137 assignments to Request must be visible when the
141 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
142 Request has been populated.
144 @return Otherwise, invalid or unsupported parameters were
145 detected. Status codes are meant for direct forwarding
146 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
154 IN CONST VSCSI_DEV
*Dev
,
157 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
158 OUT
volatile VIRTIO_SCSI_REQ
*Request
165 // bidirectional transfer was requested, but the host doesn't support it
167 (Packet
->InTransferLength
> 0 && Packet
->OutTransferLength
> 0 &&
168 !Dev
->InOutSupported
) ||
171 // a target / LUN was addressed that's impossible to encode for the host
173 Target
> 0xFF || Lun
>= 0x4000 ||
176 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
178 Packet
->CdbLength
> VIRTIO_SCSI_CDB_SIZE
||
181 // From virtio-0.9.5, 2.3.2 Descriptor Table:
182 // "no descriptor chain may be more than 2^32 bytes long in total".
184 (UINT64
) Packet
->InTransferLength
+ Packet
->OutTransferLength
> SIZE_1GB
188 // this error code doesn't require updates to the Packet output fields
190 return EFI_UNSUPPORTED
;
195 // addressed invalid device
197 Target
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
||
200 // invalid direction (there doesn't seem to be a macro for the "no data
201 // transferred" "direction", eg. for TEST UNIT READY)
203 Packet
->DataDirection
> EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
||
206 // trying to receive, but destination pointer is NULL, or contradicting
207 // transfer direction
209 (Packet
->InTransferLength
> 0 &&
210 (Packet
->InDataBuffer
== NULL
||
211 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_WRITE
216 // trying to send, but source pointer is NULL, or contradicting transfer
219 (Packet
->OutTransferLength
> 0 &&
220 (Packet
->OutDataBuffer
== NULL
||
221 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
227 // this error code doesn't require updates to the Packet output fields
229 return EFI_INVALID_PARAMETER
;
233 // Catch oversized requests eagerly. If this condition evaluates to false,
234 // then the combined size of a bidirectional request will not exceed the
235 // virtio-scsi device's transfer limit either.
237 if (ALIGN_VALUE (Packet
->OutTransferLength
, 512) / 512
238 > Dev
->MaxSectors
/ 2 ||
239 ALIGN_VALUE (Packet
->InTransferLength
, 512) / 512
240 > Dev
->MaxSectors
/ 2) {
241 Packet
->InTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
242 Packet
->OutTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
243 Packet
->HostAdapterStatus
=
244 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
245 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
246 Packet
->SenseDataLength
= 0;
247 return EFI_BAD_BUFFER_SIZE
;
251 // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,
252 // Device Operation: request queues
255 Request
->Lun
[1] = (UINT8
) Target
;
256 Request
->Lun
[2] = (UINT8
) (((UINT32
)Lun
>> 8) | 0x40);
257 Request
->Lun
[3] = (UINT8
) Lun
;
260 // CopyMem() would cast away the "volatile" qualifier before access, which is
261 // undefined behavior (ISO C99 6.7.3p5)
263 for (Idx
= 0; Idx
< Packet
->CdbLength
; ++Idx
) {
264 Request
->Cdb
[Idx
] = ((UINT8
*) Packet
->Cdb
)[Idx
];
273 Parse the virtio-scsi device's response, translate it to an EFI status code,
274 and update the Extended SCSI Pass Thru Protocol packet, to be returned by
275 the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
277 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
278 been translated to a virtio-scsi request with
279 PopulateRequest(), and processed by the host. On
280 output this parameter is updated with response or
283 @param[in] Response The virtio-scsi response structure to parse. We expect
284 it to come from a virtio ring, thus it is qualified
288 @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
289 Extended SCSI Pass Thru Protocol.
296 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
297 IN CONST
volatile VIRTIO_SCSI_RESP
*Response
300 UINTN ResponseSenseLen
;
304 // return sense data (length and contents) in all cases, truncated if needed
306 ResponseSenseLen
= MIN (Response
->SenseLen
, VIRTIO_SCSI_SENSE_SIZE
);
307 if (Packet
->SenseDataLength
> ResponseSenseLen
) {
308 Packet
->SenseDataLength
= (UINT8
) ResponseSenseLen
;
310 for (Idx
= 0; Idx
< Packet
->SenseDataLength
; ++Idx
) {
311 ((UINT8
*) Packet
->SenseData
)[Idx
] = Response
->Sense
[Idx
];
315 // Report actual transfer lengths. The logic below covers all three
316 // DataDirections (read, write, bidirectional).
320 // | write ^ @ Residual (unprocessed)
322 // -+- @ OutTransferLength -+- @ InTransferLength
326 // V @ OutTransferLength + InTransferLength -+- @ 0
328 if (Response
->Residual
<= Packet
->InTransferLength
) {
329 Packet
->InTransferLength
-= Response
->Residual
;
332 Packet
->OutTransferLength
-= Response
->Residual
- Packet
->InTransferLength
;
333 Packet
->InTransferLength
= 0;
337 // report target status in all cases
339 Packet
->TargetStatus
= Response
->Status
;
342 // host adapter status and function return value depend on virtio-scsi
345 switch (Response
->Response
) {
346 case VIRTIO_SCSI_S_OK
:
347 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
350 case VIRTIO_SCSI_S_OVERRUN
:
351 Packet
->HostAdapterStatus
=
352 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
355 case VIRTIO_SCSI_S_BAD_TARGET
:
357 // This is non-intuitive but explicitly required by the
358 // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
359 // disconnected (but otherwise valid) target / LUN addresses.
361 Packet
->HostAdapterStatus
=
362 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND
;
365 case VIRTIO_SCSI_S_RESET
:
366 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
369 case VIRTIO_SCSI_S_BUSY
:
370 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
371 return EFI_NOT_READY
;
374 // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
375 // intentional as well, not an oversight.
377 case VIRTIO_SCSI_S_ABORTED
:
378 case VIRTIO_SCSI_S_TRANSPORT_FAILURE
:
379 case VIRTIO_SCSI_S_TARGET_FAILURE
:
380 case VIRTIO_SCSI_S_NEXUS_FAILURE
:
381 case VIRTIO_SCSI_S_FAILURE
:
383 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
386 return EFI_DEVICE_ERROR
;
391 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
392 // for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
393 // - 14.1 SCSI Driver Model Overview,
394 // - 14.7 Extended SCSI Pass Thru Protocol.
400 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
403 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
404 IN EFI_EVENT Event OPTIONAL
410 volatile VIRTIO_SCSI_REQ Request
;
411 volatile VIRTIO_SCSI_RESP Response
;
412 DESC_INDICES Indices
;
414 ZeroMem ((VOID
*) &Request
, sizeof (Request
));
415 ZeroMem ((VOID
*) &Response
, sizeof (Response
));
417 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
418 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
420 Status
= PopulateRequest (Dev
, TargetValue
, Lun
, Packet
, &Request
);
421 if (EFI_ERROR (Status
)) {
425 VirtioPrepare (&Dev
->Ring
, &Indices
);
428 // preset a host status for ourselves that we do not accept as success
430 Response
.Response
= VIRTIO_SCSI_S_FAILURE
;
433 // ensured by VirtioScsiInit() -- this predicate, in combination with the
434 // lock-step progress, ensures we don't have to track free descriptors.
436 ASSERT (Dev
->Ring
.QueueSize
>= 4);
441 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) &Request
, sizeof Request
,
442 VRING_DESC_F_NEXT
, &Indices
);
445 // enqueue "dataout" if any
447 if (Packet
->OutTransferLength
> 0) {
448 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) Packet
->OutDataBuffer
,
449 Packet
->OutTransferLength
, VRING_DESC_F_NEXT
, &Indices
);
453 // enqueue Response, to be written by the host
455 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) &Response
, sizeof Response
,
456 VRING_DESC_F_WRITE
| (Packet
->InTransferLength
> 0 ?
457 VRING_DESC_F_NEXT
: 0),
461 // enqueue "datain" if any, to be written by the host
463 if (Packet
->InTransferLength
> 0) {
464 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) Packet
->InDataBuffer
,
465 Packet
->InTransferLength
, VRING_DESC_F_WRITE
, &Indices
);
468 // If kicking the host fails, we must fake a host adapter error.
469 // EFI_NOT_READY would save us the effort, but it would also suggest that the
472 if (VirtioFlush (Dev
->VirtIo
, VIRTIO_SCSI_REQUEST_QUEUE
, &Dev
->Ring
,
473 &Indices
, NULL
) != EFI_SUCCESS
) {
474 Packet
->InTransferLength
= 0;
475 Packet
->OutTransferLength
= 0;
476 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
477 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
478 Packet
->SenseDataLength
= 0;
479 return EFI_DEVICE_ERROR
;
482 return ParseResponse (Packet
, &Response
);
488 VirtioScsiGetNextTargetLun (
489 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
490 IN OUT UINT8
**TargetPointer
,
500 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
502 Target
= *TargetPointer
;
505 // Search for first non-0xFF byte. If not found, return first target & LUN.
507 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
509 if (Idx
== TARGET_MAX_BYTES
) {
510 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
516 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
518 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
521 // increment (target, LUN) pair if valid on input
523 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
524 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
525 return EFI_INVALID_PARAMETER
;
528 if (*Lun
< Dev
->MaxLun
) {
533 if (LastTarget
< Dev
->MaxTarget
) {
536 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
540 return EFI_NOT_FOUND
;
546 VirtioScsiBuildDevicePath (
547 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
550 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
555 SCSI_DEVICE_PATH
*ScsiDevicePath
;
557 if (DevicePath
== NULL
) {
558 return EFI_INVALID_PARAMETER
;
561 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
562 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
563 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
|| Lun
> 0xFFFF) {
564 return EFI_NOT_FOUND
;
567 ScsiDevicePath
= AllocatePool (sizeof *ScsiDevicePath
);
568 if (ScsiDevicePath
== NULL
) {
569 return EFI_OUT_OF_RESOURCES
;
572 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
573 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
574 ScsiDevicePath
->Header
.Length
[0] = (UINT8
) sizeof *ScsiDevicePath
;
575 ScsiDevicePath
->Header
.Length
[1] = (UINT8
) (sizeof *ScsiDevicePath
>> 8);
576 ScsiDevicePath
->Pun
= TargetValue
;
577 ScsiDevicePath
->Lun
= (UINT16
) Lun
;
579 *DevicePath
= &ScsiDevicePath
->Header
;
586 VirtioScsiGetTargetLun (
587 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
588 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
589 OUT UINT8
**TargetPointer
,
593 SCSI_DEVICE_PATH
*ScsiDevicePath
;
597 if (DevicePath
== NULL
|| TargetPointer
== NULL
|| *TargetPointer
== NULL
||
599 return EFI_INVALID_PARAMETER
;
602 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
603 DevicePath
->SubType
!= MSG_SCSI_DP
) {
604 return EFI_UNSUPPORTED
;
607 ScsiDevicePath
= (SCSI_DEVICE_PATH
*) DevicePath
;
608 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
609 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
610 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
611 return EFI_NOT_FOUND
;
615 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
616 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
617 // c) ScsiDevicePath->Pun is an UINT16
619 Target
= *TargetPointer
;
620 CopyMem (Target
, &ScsiDevicePath
->Pun
, 2);
621 SetMem (Target
+ 2, TARGET_MAX_BYTES
- 2, 0x00);
623 *Lun
= ScsiDevicePath
->Lun
;
630 VirtioScsiResetChannel (
631 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
634 return EFI_UNSUPPORTED
;
640 VirtioScsiResetTargetLun (
641 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
646 return EFI_UNSUPPORTED
;
652 VirtioScsiGetNextTarget (
653 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
654 IN OUT UINT8
**TargetPointer
663 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
665 Target
= *TargetPointer
;
668 // Search for first non-0xFF byte. If not found, return first target.
670 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
672 if (Idx
== TARGET_MAX_BYTES
) {
673 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
678 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
680 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
683 // increment target if valid on input
685 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
686 if (LastTarget
> Dev
->MaxTarget
) {
687 return EFI_INVALID_PARAMETER
;
690 if (LastTarget
< Dev
->MaxTarget
) {
692 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
696 return EFI_NOT_FOUND
;
704 IN OUT VSCSI_DEV
*Dev
711 UINT16 MaxChannel
; // for validation only
712 UINT32 NumQueues
; // for validation only
716 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
718 NextDevStat
= 0; // step 1 -- reset device
719 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
720 if (EFI_ERROR (Status
)) {
724 NextDevStat
|= VSTAT_ACK
; // step 2 -- acknowledge device presence
725 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
726 if (EFI_ERROR (Status
)) {
730 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
731 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
732 if (EFI_ERROR (Status
)) {
737 // Set Page Size - MMIO VirtIo Specific
739 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
740 if (EFI_ERROR (Status
)) {
745 // step 4a -- retrieve and validate features
747 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
748 if (EFI_ERROR (Status
)) {
751 Dev
->InOutSupported
= (BOOLEAN
) ((Features
& VIRTIO_SCSI_F_INOUT
) != 0);
753 Status
= VIRTIO_CFG_READ (Dev
, MaxChannel
, &MaxChannel
);
754 if (EFI_ERROR (Status
)) {
757 if (MaxChannel
!= 0) {
759 // this driver is for a single-channel virtio-scsi HBA
761 Status
= EFI_UNSUPPORTED
;
765 Status
= VIRTIO_CFG_READ (Dev
, NumQueues
, &NumQueues
);
766 if (EFI_ERROR (Status
)) {
770 Status
= EFI_UNSUPPORTED
;
774 Status
= VIRTIO_CFG_READ (Dev
, MaxTarget
, &Dev
->MaxTarget
);
775 if (EFI_ERROR (Status
)) {
778 if (Dev
->MaxTarget
> PcdGet16 (PcdVirtioScsiMaxTargetLimit
)) {
779 Dev
->MaxTarget
= PcdGet16 (PcdVirtioScsiMaxTargetLimit
);
782 Status
= VIRTIO_CFG_READ (Dev
, MaxLun
, &Dev
->MaxLun
);
783 if (EFI_ERROR (Status
)) {
786 if (Dev
->MaxLun
> PcdGet32 (PcdVirtioScsiMaxLunLimit
)) {
787 Dev
->MaxLun
= PcdGet32 (PcdVirtioScsiMaxLunLimit
);
790 Status
= VIRTIO_CFG_READ (Dev
, MaxSectors
, &Dev
->MaxSectors
);
791 if (EFI_ERROR (Status
)) {
794 if (Dev
->MaxSectors
< 2) {
796 // We must be able to halve it for bidirectional transfers
797 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
799 Status
= EFI_UNSUPPORTED
;
803 Features
&= VIRTIO_SCSI_F_INOUT
| VIRTIO_F_VERSION_1
;
806 // In virtio-1.0, feature negotiation is expected to complete before queue
807 // discovery, and the device can also reject the selected set of features.
809 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
810 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
811 if (EFI_ERROR (Status
)) {
817 // step 4b -- allocate request virtqueue
819 Status
= Dev
->VirtIo
->SetQueueSel (Dev
->VirtIo
, VIRTIO_SCSI_REQUEST_QUEUE
);
820 if (EFI_ERROR (Status
)) {
823 Status
= Dev
->VirtIo
->GetQueueNumMax (Dev
->VirtIo
, &QueueSize
);
824 if (EFI_ERROR (Status
)) {
828 // VirtioScsiPassThru() uses at most four descriptors
831 Status
= EFI_UNSUPPORTED
;
835 Status
= VirtioRingInit (QueueSize
, &Dev
->Ring
);
836 if (EFI_ERROR (Status
)) {
841 // Additional steps for MMIO: align the queue appropriately, and set the
842 // size. If anything fails from here on, we must release the ring resources.
844 Status
= Dev
->VirtIo
->SetQueueNum (Dev
->VirtIo
, QueueSize
);
845 if (EFI_ERROR (Status
)) {
849 Status
= Dev
->VirtIo
->SetQueueAlign (Dev
->VirtIo
, EFI_PAGE_SIZE
);
850 if (EFI_ERROR (Status
)) {
855 // step 4c -- Report GPFN (guest-physical frame number) of queue.
857 Status
= Dev
->VirtIo
->SetQueueAddress (Dev
->VirtIo
, &Dev
->Ring
);
858 if (EFI_ERROR (Status
)) {
863 // step 5 -- Report understood features and guest-tuneables.
865 if (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
866 Features
&= ~(UINT64
)VIRTIO_F_VERSION_1
;
867 Status
= Dev
->VirtIo
->SetGuestFeatures (Dev
->VirtIo
, Features
);
868 if (EFI_ERROR (Status
)) {
874 // We expect these maximum sizes from the host. Since they are
875 // guest-negotiable, ask for them rather than just checking them.
877 Status
= VIRTIO_CFG_WRITE (Dev
, CdbSize
, VIRTIO_SCSI_CDB_SIZE
);
878 if (EFI_ERROR (Status
)) {
881 Status
= VIRTIO_CFG_WRITE (Dev
, SenseSize
, VIRTIO_SCSI_SENSE_SIZE
);
882 if (EFI_ERROR (Status
)) {
887 // step 6 -- initialization complete
889 NextDevStat
|= VSTAT_DRIVER_OK
;
890 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
891 if (EFI_ERROR (Status
)) {
896 // populate the exported interface's attributes
898 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
899 Dev
->PassThru
.PassThru
= &VirtioScsiPassThru
;
900 Dev
->PassThru
.GetNextTargetLun
= &VirtioScsiGetNextTargetLun
;
901 Dev
->PassThru
.BuildDevicePath
= &VirtioScsiBuildDevicePath
;
902 Dev
->PassThru
.GetTargetLun
= &VirtioScsiGetTargetLun
;
903 Dev
->PassThru
.ResetChannel
= &VirtioScsiResetChannel
;
904 Dev
->PassThru
.ResetTargetLun
= &VirtioScsiResetTargetLun
;
905 Dev
->PassThru
.GetNextTarget
= &VirtioScsiGetNextTarget
;
908 // AdapterId is a target for which no handle will be created during bus scan.
909 // Prevent any conflict with real devices.
911 Dev
->PassThruMode
.AdapterId
= 0xFFFFFFFF;
914 // Set both physical and logical attributes for non-RAID SCSI channel. See
915 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
916 // SCSI Pass Thru Protocol.
918 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
919 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
922 // no restriction on transfer buffer alignment
924 Dev
->PassThruMode
.IoAlign
= 0;
929 VirtioRingUninit (&Dev
->Ring
);
933 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
934 // Status. VirtIo access failure here should not mask the original error.
936 NextDevStat
|= VSTAT_FAILED
;
937 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
939 Dev
->InOutSupported
= FALSE
;
944 return Status
; // reached only via Failed above
952 IN OUT VSCSI_DEV
*Dev
956 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
957 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
958 // the old comms area.
960 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
962 Dev
->InOutSupported
= FALSE
;
967 VirtioRingUninit (&Dev
->Ring
);
969 SetMem (&Dev
->PassThru
, sizeof Dev
->PassThru
, 0x00);
970 SetMem (&Dev
->PassThruMode
, sizeof Dev
->PassThruMode
, 0x00);
975 // Event notification function enqueued by ExitBootServices().
989 // Reset the device. This causes the hypervisor to forget about the virtio
992 // We allocated said ring in EfiBootServicesData type memory, and code
993 // executing after ExitBootServices() is permitted to overwrite it.
996 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
1001 // Probe, start and stop functions of this driver, called by the DXE core for
1002 // specific devices.
1004 // The following specifications document these interfaces:
1005 // - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
1006 // - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
1008 // The implementation follows:
1009 // - Driver Writer's Guide for UEFI 2.3.1 v1.01
1010 // - 5.1.3.4 OpenProtocol() and CloseProtocol()
1011 // - UEFI Spec 2.3.1 + Errata C
1012 // - 6.3 Protocol Handler Services
1017 VirtioScsiDriverBindingSupported (
1018 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1019 IN EFI_HANDLE DeviceHandle
,
1020 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1024 VIRTIO_DEVICE_PROTOCOL
*VirtIo
;
1027 // Attempt to open the device with the VirtIo set of interfaces. On success,
1028 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
1029 // attempts (EFI_ALREADY_STARTED).
1031 Status
= gBS
->OpenProtocol (
1032 DeviceHandle
, // candidate device
1033 &gVirtioDeviceProtocolGuid
, // for generic VirtIo access
1034 (VOID
**)&VirtIo
, // handle to instantiate
1035 This
->DriverBindingHandle
, // requestor driver identity
1036 DeviceHandle
, // ControllerHandle, according to
1037 // the UEFI Driver Model
1038 EFI_OPEN_PROTOCOL_BY_DRIVER
// get exclusive VirtIo access to
1039 // the device; to be released
1041 if (EFI_ERROR (Status
)) {
1045 if (VirtIo
->SubSystemDeviceId
!= VIRTIO_SUBSYSTEM_SCSI_HOST
) {
1046 Status
= EFI_UNSUPPORTED
;
1050 // We needed VirtIo access only transitorily, to see whether we support the
1053 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1054 This
->DriverBindingHandle
, DeviceHandle
);
1061 VirtioScsiDriverBindingStart (
1062 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1063 IN EFI_HANDLE DeviceHandle
,
1064 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1070 Dev
= (VSCSI_DEV
*) AllocateZeroPool (sizeof *Dev
);
1072 return EFI_OUT_OF_RESOURCES
;
1075 Status
= gBS
->OpenProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1076 (VOID
**)&Dev
->VirtIo
, This
->DriverBindingHandle
,
1077 DeviceHandle
, EFI_OPEN_PROTOCOL_BY_DRIVER
);
1078 if (EFI_ERROR (Status
)) {
1079 goto FreeVirtioScsi
;
1083 // VirtIo access granted, configure virtio-scsi device.
1085 Status
= VirtioScsiInit (Dev
);
1086 if (EFI_ERROR (Status
)) {
1090 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_CALLBACK
,
1091 &VirtioScsiExitBoot
, Dev
, &Dev
->ExitBoot
);
1092 if (EFI_ERROR (Status
)) {
1097 // Setup complete, attempt to export the driver instance's PassThru
1100 Dev
->Signature
= VSCSI_SIG
;
1101 Status
= gBS
->InstallProtocolInterface (&DeviceHandle
,
1102 &gEfiExtScsiPassThruProtocolGuid
, EFI_NATIVE_INTERFACE
,
1104 if (EFI_ERROR (Status
)) {
1111 gBS
->CloseEvent (Dev
->ExitBoot
);
1114 VirtioScsiUninit (Dev
);
1117 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1118 This
->DriverBindingHandle
, DeviceHandle
);
1129 VirtioScsiDriverBindingStop (
1130 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1131 IN EFI_HANDLE DeviceHandle
,
1132 IN UINTN NumberOfChildren
,
1133 IN EFI_HANDLE
*ChildHandleBuffer
1137 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1140 Status
= gBS
->OpenProtocol (
1141 DeviceHandle
, // candidate device
1142 &gEfiExtScsiPassThruProtocolGuid
, // retrieve the SCSI iface
1143 (VOID
**)&PassThru
, // target pointer
1144 This
->DriverBindingHandle
, // requestor driver ident.
1145 DeviceHandle
, // lookup req. for dev.
1146 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// lookup only, no new ref.
1148 if (EFI_ERROR (Status
)) {
1152 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (PassThru
);
1155 // Handle Stop() requests for in-use driver instances gracefully.
1157 Status
= gBS
->UninstallProtocolInterface (DeviceHandle
,
1158 &gEfiExtScsiPassThruProtocolGuid
, &Dev
->PassThru
);
1159 if (EFI_ERROR (Status
)) {
1163 gBS
->CloseEvent (Dev
->ExitBoot
);
1165 VirtioScsiUninit (Dev
);
1167 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1168 This
->DriverBindingHandle
, DeviceHandle
);
1177 // The static object that groups the Supported() (ie. probe), Start() and
1178 // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1179 // C, 10.1 EFI Driver Binding Protocol.
1181 STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding
= {
1182 &VirtioScsiDriverBindingSupported
,
1183 &VirtioScsiDriverBindingStart
,
1184 &VirtioScsiDriverBindingStop
,
1185 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1186 NULL
, // ImageHandle, to be overwritten by
1187 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
1188 NULL
// DriverBindingHandle, ditto
1193 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1194 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1195 // in English, for display on standard console devices. This is recommended for
1196 // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1197 // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1199 // Device type names ("Virtio SCSI Host Device") are not formatted because the
1200 // driver supports only that device type. Therefore the driver name suffices
1201 // for unambiguous identification.
1205 EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1206 { "eng;en", L
"Virtio SCSI Host Driver" },
1211 EFI_COMPONENT_NAME_PROTOCOL gComponentName
;
1215 VirtioScsiGetDriverName (
1216 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1218 OUT CHAR16
**DriverName
1221 return LookupUnicodeString2 (
1223 This
->SupportedLanguages
,
1226 (BOOLEAN
)(This
== &gComponentName
) // Iso639Language
1232 VirtioScsiGetDeviceName (
1233 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1234 IN EFI_HANDLE DeviceHandle
,
1235 IN EFI_HANDLE ChildHandle
,
1237 OUT CHAR16
**ControllerName
1240 return EFI_UNSUPPORTED
;
1244 EFI_COMPONENT_NAME_PROTOCOL gComponentName
= {
1245 &VirtioScsiGetDriverName
,
1246 &VirtioScsiGetDeviceName
,
1247 "eng" // SupportedLanguages, ISO 639-2 language codes
1251 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2
= {
1252 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &VirtioScsiGetDriverName
,
1253 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &VirtioScsiGetDeviceName
,
1254 "en" // SupportedLanguages, RFC 4646 language codes
1259 // Entry point of this driver.
1263 VirtioScsiEntryPoint (
1264 IN EFI_HANDLE ImageHandle
,
1265 IN EFI_SYSTEM_TABLE
*SystemTable
1268 return EfiLibInstallDriverBindingComponentName2 (