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, 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/Pci.h>
42 #include <IndustryStandard/VirtioScsi.h>
43 #include <Library/BaseMemoryLib.h>
44 #include <Library/DebugLib.h>
45 #include <Library/MemoryAllocationLib.h>
46 #include <Library/UefiBootServicesTableLib.h>
47 #include <Library/UefiLib.h>
48 #include <Library/VirtioLib.h>
50 #include "VirtioScsi.h"
54 Convenience macros to read and write region 0 IO space elements of the
55 virtio-scsi PCI device, for configuration purposes.
57 The following macros make it possible to specify only the "core parameters"
58 for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()
59 returns, the transaction will have been completed.
61 @param[in] Dev Pointer to the VSCSI_DEV structure whose PCI IO space
62 we're accessing. Dev->PciIo must be valid.
64 @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
65 configuration item to access.
67 @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the
68 selected configuration item.
70 @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the
71 value read from the configuration item. Its type must be
72 one of UINT8, UINT16, UINT32, UINT64.
75 @return Status codes returned by VirtioWrite() / VirtioRead().
79 #define VIRTIO_CFG_WRITE(Dev, Field, Value) (VirtioWrite ( \
81 OFFSET_OF_VSCSI (Field), \
82 SIZE_OF_VSCSI (Field), \
86 #define VIRTIO_CFG_READ(Dev, Field, Pointer) (VirtioRead ( \
88 OFFSET_OF_VSCSI (Field), \
89 SIZE_OF_VSCSI (Field), \
96 // UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
97 // the PassThru() interface. Beside returning a status code, the function must
98 // set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
99 // parameter on return. The following is a full list of those fields, for
100 // easier validation of PopulateRequest(), ParseResponse(), and
101 // VirtioScsiPassThru() below.
103 // - InTransferLength
104 // - OutTransferLength
105 // - HostAdapterStatus
110 // On any return from the PassThru() interface, these fields must be set,
111 // except if the returned status code is explicitly exempt. (Actually the
112 // implementation here conservatively sets these fields even in case not all
113 // of them would be required by the specification.)
118 Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
121 The caller is responsible for pre-zeroing the virtio-scsi request. The
122 Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
123 by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
125 @param[in] Dev The virtio-scsi host device the packet targets.
127 @param[in] Target The SCSI target controlled by the virtio-scsi host
130 @param[in] Lun The Logical Unit Number under the SCSI target.
132 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
133 function translates to a virtio-scsi request. On
134 failure this parameter relays error contents.
136 @param[out] Request The pre-zeroed virtio-scsi request to populate. This
137 parameter is volatile-qualified because we expect the
138 caller to append it to a virtio ring, thus
139 assignments to Request must be visible when the
143 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
144 Request has been populated.
146 @return Otherwise, invalid or unsupported parameters were
147 detected. Status codes are meant for direct forwarding
148 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
156 IN CONST VSCSI_DEV
*Dev
,
159 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
160 OUT
volatile VIRTIO_SCSI_REQ
*Request
167 // bidirectional transfer was requested, but the host doesn't support it
169 (Packet
->InTransferLength
> 0 && Packet
->OutTransferLength
> 0 &&
170 !Dev
->InOutSupported
) ||
173 // a target / LUN was addressed that's impossible to encode for the host
175 Target
> 0xFF || Lun
>= 0x4000 ||
178 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
180 Packet
->CdbLength
> VIRTIO_SCSI_CDB_SIZE
||
183 // From virtio-0.9.5, 2.3.2 Descriptor Table:
184 // "no descriptor chain may be more than 2^32 bytes long in total".
186 (UINT64
) Packet
->InTransferLength
+ Packet
->OutTransferLength
> SIZE_1GB
190 // this error code doesn't require updates to the Packet output fields
192 return EFI_UNSUPPORTED
;
197 // addressed invalid device
199 Target
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
||
202 // invalid direction (there doesn't seem to be a macro for the "no data
203 // transferred" "direction", eg. for TEST UNIT READY)
205 Packet
->DataDirection
> EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
||
208 // trying to receive, but destination pointer is NULL, or contradicting
209 // transfer direction
211 (Packet
->InTransferLength
> 0 &&
212 (Packet
->InDataBuffer
== NULL
||
213 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_WRITE
218 // trying to send, but source pointer is NULL, or contradicting transfer
221 (Packet
->OutTransferLength
> 0 &&
222 (Packet
->OutDataBuffer
== NULL
||
223 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
229 // this error code doesn't require updates to the Packet output fields
231 return EFI_INVALID_PARAMETER
;
235 // Catch oversized requests eagerly. If this condition evaluates to false,
236 // then the combined size of a bidirectional request will not exceed the
237 // virtio-scsi device's transfer limit either.
239 if (ALIGN_VALUE (Packet
->OutTransferLength
, 512) / 512
240 > Dev
->MaxSectors
/ 2 ||
241 ALIGN_VALUE (Packet
->InTransferLength
, 512) / 512
242 > Dev
->MaxSectors
/ 2) {
243 Packet
->InTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
244 Packet
->OutTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
245 Packet
->HostAdapterStatus
=
246 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
247 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
248 Packet
->SenseDataLength
= 0;
249 return EFI_BAD_BUFFER_SIZE
;
253 // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,
254 // Device Operation: request queues
257 Request
->Lun
[1] = (UINT8
) Target
;
258 Request
->Lun
[2] = (UINT8
) ((Lun
>> 8) | 0x40);
259 Request
->Lun
[3] = (UINT8
) Lun
;
262 // CopyMem() would cast away the "volatile" qualifier before access, which is
263 // undefined behavior (ISO C99 6.7.3p5)
265 for (Idx
= 0; Idx
< Packet
->CdbLength
; ++Idx
) {
266 Request
->Cdb
[Idx
] = ((UINT8
*) Packet
->Cdb
)[Idx
];
275 Parse the virtio-scsi device's response, translate it to an EFI status code,
276 and update the Extended SCSI Pass Thru Protocol packet, to be returned by
277 the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
279 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
280 been translated to a virtio-scsi request with
281 PopulateRequest(), and processed by the host. On
282 output this parameter is updated with response or
285 @param[in] Response The virtio-scsi response structure to parse. We expect
286 it to come from a virtio ring, thus it is qualified
290 @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
291 Extended SCSI Pass Thru Protocol.
298 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
299 IN CONST
volatile VIRTIO_SCSI_RESP
*Response
302 UINTN ResponseSenseLen
;
306 // return sense data (length and contents) in all cases, truncated if needed
308 ResponseSenseLen
= MIN (Response
->SenseLen
, VIRTIO_SCSI_SENSE_SIZE
);
309 if (Packet
->SenseDataLength
> ResponseSenseLen
) {
310 Packet
->SenseDataLength
= (UINT8
) ResponseSenseLen
;
312 for (Idx
= 0; Idx
< Packet
->SenseDataLength
; ++Idx
) {
313 ((UINT8
*) Packet
->SenseData
)[Idx
] = Response
->Sense
[Idx
];
317 // Report actual transfer lengths. The logic below covers all three
318 // DataDirections (read, write, bidirectional).
322 // | write ^ @ Residual (unprocessed)
324 // -+- @ OutTransferLength -+- @ InTransferLength
328 // V @ OutTransferLength + InTransferLength -+- @ 0
330 if (Response
->Residual
<= Packet
->InTransferLength
) {
331 Packet
->InTransferLength
-= Response
->Residual
;
334 Packet
->OutTransferLength
-= Response
->Residual
- Packet
->InTransferLength
;
335 Packet
->InTransferLength
= 0;
339 // report target status in all cases
341 Packet
->TargetStatus
= Response
->Status
;
344 // host adapter status and function return value depend on virtio-scsi
347 switch (Response
->Response
) {
348 case VIRTIO_SCSI_S_OK
:
349 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
352 case VIRTIO_SCSI_S_OVERRUN
:
353 Packet
->HostAdapterStatus
=
354 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
357 case VIRTIO_SCSI_S_BAD_TARGET
:
359 // This is non-intuitive but explicitly required by the
360 // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
361 // disconnected (but otherwise valid) target / LUN addresses.
363 Packet
->HostAdapterStatus
=
364 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND
;
367 case VIRTIO_SCSI_S_RESET
:
368 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
371 case VIRTIO_SCSI_S_BUSY
:
372 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
373 return EFI_NOT_READY
;
376 // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
377 // intentional as well, not an oversight.
379 case VIRTIO_SCSI_S_ABORTED
:
380 case VIRTIO_SCSI_S_TRANSPORT_FAILURE
:
381 case VIRTIO_SCSI_S_TARGET_FAILURE
:
382 case VIRTIO_SCSI_S_NEXUS_FAILURE
:
383 case VIRTIO_SCSI_S_FAILURE
:
385 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
388 return EFI_DEVICE_ERROR
;
393 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
394 // for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
395 // - 14.1 SCSI Driver Model Overview,
396 // - 14.7 Extended SCSI Pass Thru Protocol.
402 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
405 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
406 IN EFI_EVENT Event OPTIONAL
412 volatile VIRTIO_SCSI_REQ Request
;
413 volatile VIRTIO_SCSI_RESP Response
;
414 DESC_INDICES Indices
;
417 // Zero-initialization of Request & Response with "= { 0 };" doesn't build
418 // with gcc-4.4: "undefined reference to `memset'". Direct SetMem() is not
419 // allowed as it would cast away the volatile qualifier. Work it around.
422 VIRTIO_SCSI_REQ Request
;
423 VIRTIO_SCSI_RESP Response
;
426 SetMem (&Zero
, sizeof Zero
, 0x00);
427 Request
= Zero
.Request
;
428 Response
= Zero
.Response
;
430 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
431 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
433 Status
= PopulateRequest (Dev
, TargetValue
, Lun
, Packet
, &Request
);
434 if (EFI_ERROR (Status
)) {
438 VirtioPrepare (&Dev
->Ring
, &Indices
);
441 // preset a host status for ourselves that we do not accept as success
443 Response
.Response
= VIRTIO_SCSI_S_FAILURE
;
446 // ensured by VirtioScsiInit() -- this predicate, in combination with the
447 // lock-step progress, ensures we don't have to track free descriptors.
449 ASSERT (Dev
->Ring
.QueueSize
>= 4);
454 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) &Request
, sizeof Request
,
455 VRING_DESC_F_NEXT
, &Indices
);
458 // enqueue "dataout" if any
460 if (Packet
->OutTransferLength
> 0) {
461 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) Packet
->OutDataBuffer
,
462 Packet
->OutTransferLength
, VRING_DESC_F_NEXT
, &Indices
);
466 // enqueue Response, to be written by the host
468 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) &Response
, sizeof Response
,
469 VRING_DESC_F_WRITE
| (Packet
->InTransferLength
> 0 ?
470 VRING_DESC_F_NEXT
: 0),
474 // enqueue "datain" if any, to be written by the host
476 if (Packet
->InTransferLength
> 0) {
477 VirtioAppendDesc (&Dev
->Ring
, (UINTN
) Packet
->InDataBuffer
,
478 Packet
->InTransferLength
, VRING_DESC_F_WRITE
, &Indices
);
481 // If kicking the host fails, we must fake a host adapter error.
482 // EFI_NOT_READY would save us the effort, but it would also suggest that the
485 if (VirtioFlush (Dev
->PciIo
, VIRTIO_SCSI_REQUEST_QUEUE
, &Dev
->Ring
,
486 &Indices
) != EFI_SUCCESS
) {
487 Packet
->InTransferLength
= 0;
488 Packet
->OutTransferLength
= 0;
489 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
490 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
491 Packet
->SenseDataLength
= 0;
492 return EFI_DEVICE_ERROR
;
495 return ParseResponse (Packet
, &Response
);
501 VirtioScsiGetNextTargetLun (
502 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
503 IN OUT UINT8
**TargetPointer
,
513 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
515 Target
= *TargetPointer
;
518 // Search for first non-0xFF byte. If not found, return first target & LUN.
520 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
522 if (Idx
== TARGET_MAX_BYTES
) {
523 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
529 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
531 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
534 // increment (target, LUN) pair if valid on input
536 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
537 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
538 return EFI_INVALID_PARAMETER
;
541 if (*Lun
< Dev
->MaxLun
) {
546 if (LastTarget
< Dev
->MaxTarget
) {
549 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
553 return EFI_NOT_FOUND
;
559 VirtioScsiBuildDevicePath (
560 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
563 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
568 SCSI_DEVICE_PATH
*ScsiDevicePath
;
570 if (DevicePath
== NULL
) {
571 return EFI_INVALID_PARAMETER
;
574 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
575 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
576 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
|| Lun
> 0xFFFF) {
577 return EFI_NOT_FOUND
;
580 ScsiDevicePath
= AllocatePool (sizeof *ScsiDevicePath
);
581 if (ScsiDevicePath
== NULL
) {
582 return EFI_OUT_OF_RESOURCES
;
585 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
586 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
587 ScsiDevicePath
->Header
.Length
[0] = (UINT8
) sizeof *ScsiDevicePath
;
588 ScsiDevicePath
->Header
.Length
[1] = (UINT8
) (sizeof *ScsiDevicePath
>> 8);
589 ScsiDevicePath
->Pun
= TargetValue
;
590 ScsiDevicePath
->Lun
= (UINT16
) Lun
;
592 *DevicePath
= &ScsiDevicePath
->Header
;
599 VirtioScsiGetTargetLun (
600 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
601 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
602 OUT UINT8
**TargetPointer
,
606 SCSI_DEVICE_PATH
*ScsiDevicePath
;
610 if (DevicePath
== NULL
|| TargetPointer
== NULL
|| *TargetPointer
== NULL
||
612 return EFI_INVALID_PARAMETER
;
615 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
616 DevicePath
->SubType
!= MSG_SCSI_DP
) {
617 return EFI_UNSUPPORTED
;
620 ScsiDevicePath
= (SCSI_DEVICE_PATH
*) DevicePath
;
621 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
622 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
623 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
624 return EFI_NOT_FOUND
;
628 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
629 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
630 // c) ScsiDevicePath->Pun is an UINT16
632 Target
= *TargetPointer
;
633 CopyMem (Target
, &ScsiDevicePath
->Pun
, 2);
634 SetMem (Target
+ 2, TARGET_MAX_BYTES
- 2, 0x00);
636 *Lun
= ScsiDevicePath
->Lun
;
643 VirtioScsiResetChannel (
644 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
647 return EFI_UNSUPPORTED
;
653 VirtioScsiResetTargetLun (
654 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
659 return EFI_UNSUPPORTED
;
665 VirtioScsiGetNextTarget (
666 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
667 IN OUT UINT8
**TargetPointer
676 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
678 Target
= *TargetPointer
;
681 // Search for first non-0xFF byte. If not found, return first target.
683 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
685 if (Idx
== TARGET_MAX_BYTES
) {
686 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
691 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
693 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
696 // increment target if valid on input
698 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
699 if (LastTarget
> Dev
->MaxTarget
) {
700 return EFI_INVALID_PARAMETER
;
703 if (LastTarget
< Dev
->MaxTarget
) {
705 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
709 return EFI_NOT_FOUND
;
717 IN OUT VSCSI_DEV
*Dev
724 UINT16 MaxChannel
; // for validation only
725 UINT32 NumQueues
; // for validation only
729 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
731 NextDevStat
= 0; // step 1 -- reset device
732 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, NextDevStat
);
733 if (EFI_ERROR (Status
)) {
737 NextDevStat
|= VSTAT_ACK
; // step 2 -- acknowledge device presence
738 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, NextDevStat
);
739 if (EFI_ERROR (Status
)) {
743 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
744 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, NextDevStat
);
745 if (EFI_ERROR (Status
)) {
750 // step 4a -- retrieve and validate features
752 Status
= VIRTIO_CFG_READ (Dev
, Generic
.VhdrDeviceFeatureBits
, &Features
);
753 if (EFI_ERROR (Status
)) {
756 Dev
->InOutSupported
= !!(Features
& VIRTIO_SCSI_F_INOUT
);
758 Status
= VIRTIO_CFG_READ (Dev
, VhdrMaxChannel
, &MaxChannel
);
759 if (EFI_ERROR (Status
)) {
762 if (MaxChannel
!= 0) {
764 // this driver is for a single-channel virtio-scsi HBA
766 Status
= EFI_UNSUPPORTED
;
770 Status
= VIRTIO_CFG_READ (Dev
, VhdrNumQueues
, &NumQueues
);
771 if (EFI_ERROR (Status
)) {
775 Status
= EFI_UNSUPPORTED
;
779 Status
= VIRTIO_CFG_READ (Dev
, VhdrMaxTarget
, &Dev
->MaxTarget
);
780 if (EFI_ERROR (Status
)) {
783 if (Dev
->MaxTarget
> PcdGet16 (PcdVirtioScsiMaxTargetLimit
)) {
784 Dev
->MaxTarget
= PcdGet16 (PcdVirtioScsiMaxTargetLimit
);
787 Status
= VIRTIO_CFG_READ (Dev
, VhdrMaxLun
, &Dev
->MaxLun
);
788 if (EFI_ERROR (Status
)) {
791 if (Dev
->MaxLun
> PcdGet32 (PcdVirtioScsiMaxLunLimit
)) {
792 Dev
->MaxLun
= PcdGet32 (PcdVirtioScsiMaxLunLimit
);
795 Status
= VIRTIO_CFG_READ (Dev
, VhdrMaxSectors
, &Dev
->MaxSectors
);
796 if (EFI_ERROR (Status
)) {
799 if (Dev
->MaxSectors
< 2) {
801 // We must be able to halve it for bidirectional transfers
802 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
804 Status
= EFI_UNSUPPORTED
;
809 // step 4b -- allocate request virtqueue
811 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrQueueSelect
,
812 VIRTIO_SCSI_REQUEST_QUEUE
);
813 if (EFI_ERROR (Status
)) {
816 Status
= VIRTIO_CFG_READ (Dev
, Generic
.VhdrQueueSize
, &QueueSize
);
817 if (EFI_ERROR (Status
)) {
821 // VirtioScsiPassThru() uses at most four descriptors
824 Status
= EFI_UNSUPPORTED
;
828 Status
= VirtioRingInit (QueueSize
, &Dev
->Ring
);
829 if (EFI_ERROR (Status
)) {
834 // step 4c -- Report GPFN (guest-physical frame number) of queue. If anything
835 // fails from here on, we must release the ring resources.
837 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrQueueAddress
,
838 (UINTN
) Dev
->Ring
.Base
>> EFI_PAGE_SHIFT
);
839 if (EFI_ERROR (Status
)) {
844 // step 5 -- Report understood features and guest-tuneables. We want none of
845 // the known (or unknown) VIRTIO_SCSI_F_* or VIRTIO_F_* capabilities (see
846 // virtio-0.9.5, Appendices B and I), except bidirectional transfers.
848 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrGuestFeatureBits
,
849 Features
& VIRTIO_SCSI_F_INOUT
);
850 if (EFI_ERROR (Status
)) {
855 // We expect these maximum sizes from the host. Since they are
856 // guest-negotiable, ask for them rather than just checking them.
858 Status
= VIRTIO_CFG_WRITE (Dev
, VhdrCdbSize
, VIRTIO_SCSI_CDB_SIZE
);
859 if (EFI_ERROR (Status
)) {
862 Status
= VIRTIO_CFG_WRITE (Dev
, VhdrSenseSize
, VIRTIO_SCSI_SENSE_SIZE
);
863 if (EFI_ERROR (Status
)) {
868 // step 6 -- initialization complete
870 NextDevStat
|= VSTAT_DRIVER_OK
;
871 Status
= VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, NextDevStat
);
872 if (EFI_ERROR (Status
)) {
877 // populate the exported interface's attributes
879 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
880 Dev
->PassThru
.PassThru
= &VirtioScsiPassThru
;
881 Dev
->PassThru
.GetNextTargetLun
= &VirtioScsiGetNextTargetLun
;
882 Dev
->PassThru
.BuildDevicePath
= &VirtioScsiBuildDevicePath
;
883 Dev
->PassThru
.GetTargetLun
= &VirtioScsiGetTargetLun
;
884 Dev
->PassThru
.ResetChannel
= &VirtioScsiResetChannel
;
885 Dev
->PassThru
.ResetTargetLun
= &VirtioScsiResetTargetLun
;
886 Dev
->PassThru
.GetNextTarget
= &VirtioScsiGetNextTarget
;
889 // AdapterId is a target for which no handle will be created during bus scan.
890 // Prevent any conflict with real devices.
892 Dev
->PassThruMode
.AdapterId
= 0xFFFFFFFF;
895 // Set both physical and logical attributes for non-RAID SCSI channel. See
896 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
897 // SCSI Pass Thru Protocol.
899 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
900 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
903 // no restriction on transfer buffer alignment
905 Dev
->PassThruMode
.IoAlign
= 0;
910 VirtioRingUninit (&Dev
->Ring
);
914 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
915 // Status. PCI IO access failure here should not mask the original error.
917 NextDevStat
|= VSTAT_FAILED
;
918 VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, NextDevStat
);
920 Dev
->InOutSupported
= FALSE
;
925 return Status
; // reached only via Failed above
934 IN OUT VSCSI_DEV
*Dev
938 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
939 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
940 // the old comms area.
942 VIRTIO_CFG_WRITE (Dev
, Generic
.VhdrDeviceStatus
, 0);
944 Dev
->InOutSupported
= FALSE
;
949 VirtioRingUninit (&Dev
->Ring
);
951 SetMem (&Dev
->PassThru
, sizeof Dev
->PassThru
, 0x00);
952 SetMem (&Dev
->PassThruMode
, sizeof Dev
->PassThruMode
, 0x00);
957 // Probe, start and stop functions of this driver, called by the DXE core for
960 // The following specifications document these interfaces:
961 // - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
962 // - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
964 // The implementation follows:
965 // - Driver Writer's Guide for UEFI 2.3.1 v1.01
966 // - 5.1.3.4 OpenProtocol() and CloseProtocol()
967 // - 18 PCI Driver Design Guidelines
968 // - 18.3 PCI drivers
969 // - UEFI Spec 2.3.1 + Errata C
970 // - 6.3 Protocol Handler Services
971 // - 13.4 EFI PCI I/O Protocol
976 VirtioScsiDriverBindingSupported (
977 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
978 IN EFI_HANDLE DeviceHandle
,
979 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
983 EFI_PCI_IO_PROTOCOL
*PciIo
;
987 // Attempt to open the device with the PciIo set of interfaces. On success,
988 // the protocol is "instantiated" for the PCI device. Covers duplicate open
989 // attempts (EFI_ALREADY_STARTED).
991 Status
= gBS
->OpenProtocol (
992 DeviceHandle
, // candidate device
993 &gEfiPciIoProtocolGuid
, // for generic PCI access
994 (VOID
**)&PciIo
, // handle to instantiate
995 This
->DriverBindingHandle
, // requestor driver identity
996 DeviceHandle
, // ControllerHandle, according to
997 // the UEFI Driver Model
998 EFI_OPEN_PROTOCOL_BY_DRIVER
// get exclusive PciIo access to
999 // the device; to be released
1001 if (EFI_ERROR (Status
)) {
1006 // Read entire PCI configuration header for more extensive check ahead.
1008 Status
= PciIo
->Pci
.Read (
1009 PciIo
, // (protocol, device)
1011 EfiPciIoWidthUint32
, // access width & copy
1014 sizeof Pci
/ sizeof (UINT32
), // Count
1015 &Pci
// target buffer
1018 if (Status
== EFI_SUCCESS
) {
1020 // virtio-0.9.5, 2.1 PCI Discovery
1022 Status
= (Pci
.Hdr
.VendorId
== 0x1AF4 &&
1023 Pci
.Hdr
.DeviceId
>= 0x1000 && Pci
.Hdr
.DeviceId
<= 0x103F &&
1024 Pci
.Hdr
.RevisionID
== 0x00 &&
1025 Pci
.Device
.SubsystemID
== 0x08) ? EFI_SUCCESS
: EFI_UNSUPPORTED
;
1029 // We needed PCI IO access only transitorily, to see whether we support the
1032 gBS
->CloseProtocol (DeviceHandle
, &gEfiPciIoProtocolGuid
,
1033 This
->DriverBindingHandle
, DeviceHandle
);
1040 VirtioScsiDriverBindingStart (
1041 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1042 IN EFI_HANDLE DeviceHandle
,
1043 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1049 Dev
= (VSCSI_DEV
*) AllocateZeroPool (sizeof *Dev
);
1051 return EFI_OUT_OF_RESOURCES
;
1054 Status
= gBS
->OpenProtocol (DeviceHandle
, &gEfiPciIoProtocolGuid
,
1055 (VOID
**)&Dev
->PciIo
, This
->DriverBindingHandle
,
1056 DeviceHandle
, EFI_OPEN_PROTOCOL_BY_DRIVER
);
1057 if (EFI_ERROR (Status
)) {
1058 goto FreeVirtioScsi
;
1062 // We must retain and ultimately restore the original PCI attributes of the
1063 // device. See Driver Writer's Guide for UEFI 2.3.1 v1.01, 18.3 PCI drivers /
1064 // 18.3.2 Start() and Stop().
1066 // The third parameter ("Attributes", input) is ignored by the Get operation.
1067 // The fourth parameter ("Result", output) is ignored by the Enable and Set
1070 // For virtio-scsi we only need IO space access.
1072 Status
= Dev
->PciIo
->Attributes (Dev
->PciIo
, EfiPciIoAttributeOperationGet
,
1073 0, &Dev
->OriginalPciAttributes
);
1074 if (EFI_ERROR (Status
)) {
1078 Status
= Dev
->PciIo
->Attributes (Dev
->PciIo
,
1079 EfiPciIoAttributeOperationEnable
,
1080 EFI_PCI_IO_ATTRIBUTE_IO
, NULL
);
1081 if (EFI_ERROR (Status
)) {
1086 // PCI IO access granted, configure virtio-scsi device.
1088 Status
= VirtioScsiInit (Dev
);
1089 if (EFI_ERROR (Status
)) {
1090 goto RestorePciAttributes
;
1094 // Setup complete, attempt to export the driver instance's PassThru
1097 Dev
->Signature
= VSCSI_SIG
;
1098 Status
= gBS
->InstallProtocolInterface (&DeviceHandle
,
1099 &gEfiExtScsiPassThruProtocolGuid
, EFI_NATIVE_INTERFACE
,
1101 if (EFI_ERROR (Status
)) {
1108 VirtioScsiUninit (Dev
);
1110 RestorePciAttributes
:
1111 Dev
->PciIo
->Attributes (Dev
->PciIo
, EfiPciIoAttributeOperationSet
,
1112 Dev
->OriginalPciAttributes
, NULL
);
1115 gBS
->CloseProtocol (DeviceHandle
, &gEfiPciIoProtocolGuid
,
1116 This
->DriverBindingHandle
, DeviceHandle
);
1127 VirtioScsiDriverBindingStop (
1128 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1129 IN EFI_HANDLE DeviceHandle
,
1130 IN UINTN NumberOfChildren
,
1131 IN EFI_HANDLE
*ChildHandleBuffer
1135 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1138 Status
= gBS
->OpenProtocol (
1139 DeviceHandle
, // candidate device
1140 &gEfiExtScsiPassThruProtocolGuid
, // retrieve the SCSI iface
1141 (VOID
**)&PassThru
, // target pointer
1142 This
->DriverBindingHandle
, // requestor driver ident.
1143 DeviceHandle
, // lookup req. for dev.
1144 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// lookup only, no new ref.
1146 if (EFI_ERROR (Status
)) {
1150 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (PassThru
);
1153 // Handle Stop() requests for in-use driver instances gracefully.
1155 Status
= gBS
->UninstallProtocolInterface (DeviceHandle
,
1156 &gEfiExtScsiPassThruProtocolGuid
, &Dev
->PassThru
);
1157 if (EFI_ERROR (Status
)) {
1161 VirtioScsiUninit (Dev
);
1163 Dev
->PciIo
->Attributes (Dev
->PciIo
, EfiPciIoAttributeOperationSet
,
1164 Dev
->OriginalPciAttributes
, NULL
);
1166 gBS
->CloseProtocol (DeviceHandle
, &gEfiPciIoProtocolGuid
,
1167 This
->DriverBindingHandle
, DeviceHandle
);
1176 // The static object that groups the Supported() (ie. probe), Start() and
1177 // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1178 // C, 10.1 EFI Driver Binding Protocol.
1180 STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding
= {
1181 &VirtioScsiDriverBindingSupported
,
1182 &VirtioScsiDriverBindingStart
,
1183 &VirtioScsiDriverBindingStop
,
1184 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1185 NULL
, // ImageHandle, to be overwritten by
1186 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
1187 NULL
// DriverBindingHandle, ditto
1192 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1193 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1194 // in English, for display on standard console devices. This is recommended for
1195 // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1196 // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1198 // Device type names ("Virtio SCSI Host Device") are not formatted because the
1199 // driver supports only that device type. Therefore the driver name suffices
1200 // for unambiguous identification.
1204 EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1205 { "eng;en", L
"Virtio SCSI Host Driver" },
1210 EFI_COMPONENT_NAME_PROTOCOL gComponentName
;
1214 VirtioScsiGetDriverName (
1215 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1217 OUT CHAR16
**DriverName
1220 return LookupUnicodeString2 (
1222 This
->SupportedLanguages
,
1225 (BOOLEAN
)(This
== &gComponentName
) // Iso639Language
1231 VirtioScsiGetDeviceName (
1232 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1233 IN EFI_HANDLE DeviceHandle
,
1234 IN EFI_HANDLE ChildHandle
,
1236 OUT CHAR16
**ControllerName
1239 return EFI_UNSUPPORTED
;
1243 EFI_COMPONENT_NAME_PROTOCOL gComponentName
= {
1244 &VirtioScsiGetDriverName
,
1245 &VirtioScsiGetDeviceName
,
1246 "eng" // SupportedLanguages, ISO 639-2 language codes
1250 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2
= {
1251 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &VirtioScsiGetDriverName
,
1252 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &VirtioScsiGetDeviceName
,
1253 "en" // SupportedLanguages, RFC 4646 language codes
1258 // Entry point of this driver.
1262 VirtioScsiEntryPoint (
1263 IN EFI_HANDLE ImageHandle
,
1264 IN EFI_SYSTEM_TABLE
*SystemTable
1267 return EfiLibInstallDriverBindingComponentName2 (