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 - 2018, Intel Corporation. All rights reserved.<BR>
30 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
32 SPDX-License-Identifier: BSD-2-Clause-Patent
36 #include <IndustryStandard/VirtioScsi.h>
37 #include <Library/BaseMemoryLib.h>
38 #include <Library/DebugLib.h>
39 #include <Library/MemoryAllocationLib.h>
40 #include <Library/UefiBootServicesTableLib.h>
41 #include <Library/UefiLib.h>
42 #include <Library/VirtioLib.h>
44 #include "VirtioScsi.h"
48 Convenience macros to read and write configuration elements of the
49 virtio-scsi VirtIo device.
51 The following macros make it possible to specify only the "core parameters"
52 for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()
53 returns, the transaction will have been completed.
55 @param[in] Dev Pointer to the VSCSI_DEV structure.
57 @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi
58 configuration item to access.
60 @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the
61 selected configuration item.
63 @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the
64 value read from the configuration item. Its type must be
65 one of UINT8, UINT16, UINT32, UINT64.
68 @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice().
72 #define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \
74 OFFSET_OF_VSCSI (Field), \
75 SIZE_OF_VSCSI (Field), \
79 #define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \
81 OFFSET_OF_VSCSI (Field), \
82 SIZE_OF_VSCSI (Field), \
88 // UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
89 // the PassThru() interface. Beside returning a status code, the function must
90 // set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
91 // parameter on return. The following is a full list of those fields, for
92 // easier validation of PopulateRequest(), ParseResponse(), and
93 // ReportHostAdapterError() below.
96 // - OutTransferLength
97 // - HostAdapterStatus
102 // On any return from the PassThru() interface, these fields must be set,
103 // except if the returned status code is explicitly exempt. (Actually the
104 // implementation here conservatively sets these fields even in case not all
105 // of them would be required by the specification.)
110 Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
113 The caller is responsible for pre-zeroing the virtio-scsi request. The
114 Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
115 by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
117 @param[in] Dev The virtio-scsi host device the packet targets.
119 @param[in] Target The SCSI target controlled by the virtio-scsi host
122 @param[in] Lun The Logical Unit Number under the SCSI target.
124 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
125 function translates to a virtio-scsi request. On
126 failure this parameter relays error contents.
128 @param[out] Request The pre-zeroed virtio-scsi request to populate. This
129 parameter is volatile-qualified because we expect the
130 caller to append it to a virtio ring, thus
131 assignments to Request must be visible when the
135 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
136 Request has been populated.
138 @return Otherwise, invalid or unsupported parameters were
139 detected. Status codes are meant for direct forwarding
140 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
148 IN CONST VSCSI_DEV
*Dev
,
151 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
152 OUT
volatile VIRTIO_SCSI_REQ
*Request
159 // bidirectional transfer was requested, but the host doesn't support it
161 ((Packet
->InTransferLength
> 0) && (Packet
->OutTransferLength
> 0) &&
162 !Dev
->InOutSupported
) ||
165 // a target / LUN was addressed that's impossible to encode for the host
167 (Target
> 0xFF) || (Lun
>= 0x4000) ||
170 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
172 (Packet
->CdbLength
> VIRTIO_SCSI_CDB_SIZE
) ||
175 // From virtio-0.9.5, 2.3.2 Descriptor Table:
176 // "no descriptor chain may be more than 2^32 bytes long in total".
178 ((UINT64
)Packet
->InTransferLength
+ Packet
->OutTransferLength
> SIZE_1GB
)
182 // this error code doesn't require updates to the Packet output fields
184 return EFI_UNSUPPORTED
;
189 // addressed invalid device
191 (Target
> Dev
->MaxTarget
) || (Lun
> Dev
->MaxLun
) ||
194 // invalid direction (there doesn't seem to be a macro for the "no data
195 // transferred" "direction", eg. for TEST UNIT READY)
197 (Packet
->DataDirection
> EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
) ||
200 // trying to receive, but destination pointer is NULL, or contradicting
201 // transfer direction
203 ((Packet
->InTransferLength
> 0) &&
204 ((Packet
->InDataBuffer
== NULL
) ||
205 (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_WRITE
)
210 // trying to send, but source pointer is NULL, or contradicting transfer
213 ((Packet
->OutTransferLength
> 0) &&
214 ((Packet
->OutDataBuffer
== NULL
) ||
215 (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
)
221 // this error code doesn't require updates to the Packet output fields
223 return EFI_INVALID_PARAMETER
;
227 // Catch oversized requests eagerly. If this condition evaluates to false,
228 // then the combined size of a bidirectional request will not exceed the
229 // virtio-scsi device's transfer limit either.
231 if ((ALIGN_VALUE (Packet
->OutTransferLength
, 512) / 512
232 > Dev
->MaxSectors
/ 2) ||
233 (ALIGN_VALUE (Packet
->InTransferLength
, 512) / 512
234 > Dev
->MaxSectors
/ 2))
236 Packet
->InTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
237 Packet
->OutTransferLength
= (Dev
->MaxSectors
/ 2) * 512;
238 Packet
->HostAdapterStatus
=
239 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
240 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
241 Packet
->SenseDataLength
= 0;
242 return EFI_BAD_BUFFER_SIZE
;
246 // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,
247 // Device Operation: request queues
250 Request
->Lun
[1] = (UINT8
)Target
;
251 Request
->Lun
[2] = (UINT8
)(((UINT32
)Lun
>> 8) | 0x40);
252 Request
->Lun
[3] = (UINT8
)Lun
;
255 // CopyMem() would cast away the "volatile" qualifier before access, which is
256 // undefined behavior (ISO C99 6.7.3p5)
258 for (Idx
= 0; Idx
< Packet
->CdbLength
; ++Idx
) {
259 Request
->Cdb
[Idx
] = ((UINT8
*)Packet
->Cdb
)[Idx
];
267 Parse the virtio-scsi device's response, translate it to an EFI status code,
268 and update the Extended SCSI Pass Thru Protocol packet, to be returned by
269 the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
271 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
272 been translated to a virtio-scsi request with
273 PopulateRequest(), and processed by the host. On
274 output this parameter is updated with response or
277 @param[in] Response The virtio-scsi response structure to parse. We expect
278 it to come from a virtio ring, thus it is qualified
282 @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
283 Extended SCSI Pass Thru Protocol.
290 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
291 IN CONST
volatile VIRTIO_SCSI_RESP
*Response
294 UINTN ResponseSenseLen
;
298 // return sense data (length and contents) in all cases, truncated if needed
300 ResponseSenseLen
= MIN (Response
->SenseLen
, VIRTIO_SCSI_SENSE_SIZE
);
301 if (Packet
->SenseDataLength
> ResponseSenseLen
) {
302 Packet
->SenseDataLength
= (UINT8
)ResponseSenseLen
;
305 for (Idx
= 0; Idx
< Packet
->SenseDataLength
; ++Idx
) {
306 ((UINT8
*)Packet
->SenseData
)[Idx
] = Response
->Sense
[Idx
];
310 // Report actual transfer lengths. The logic below covers all three
311 // DataDirections (read, write, bidirectional).
315 // | write ^ @ Residual (unprocessed)
317 // -+- @ OutTransferLength -+- @ InTransferLength
321 // V @ OutTransferLength + InTransferLength -+- @ 0
323 if (Response
->Residual
<= Packet
->InTransferLength
) {
324 Packet
->InTransferLength
-= Response
->Residual
;
326 Packet
->OutTransferLength
-= Response
->Residual
- Packet
->InTransferLength
;
327 Packet
->InTransferLength
= 0;
331 // report target status in all cases
333 Packet
->TargetStatus
= Response
->Status
;
336 // host adapter status and function return value depend on virtio-scsi
339 switch (Response
->Response
) {
340 case VIRTIO_SCSI_S_OK
:
341 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
344 case VIRTIO_SCSI_S_OVERRUN
:
345 Packet
->HostAdapterStatus
=
346 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
349 case VIRTIO_SCSI_S_BAD_TARGET
:
351 // This is non-intuitive but explicitly required by the
352 // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
353 // disconnected (but otherwise valid) target / LUN addresses.
355 Packet
->HostAdapterStatus
=
356 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND
;
359 case VIRTIO_SCSI_S_RESET
:
360 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
363 case VIRTIO_SCSI_S_BUSY
:
364 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
365 return EFI_NOT_READY
;
368 // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
369 // intentional as well, not an oversight.
371 case VIRTIO_SCSI_S_ABORTED
:
372 case VIRTIO_SCSI_S_TRANSPORT_FAILURE
:
373 case VIRTIO_SCSI_S_TARGET_FAILURE
:
374 case VIRTIO_SCSI_S_NEXUS_FAILURE
:
375 case VIRTIO_SCSI_S_FAILURE
:
377 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
380 return EFI_DEVICE_ERROR
;
385 The function can be used to create a fake host adapter error.
387 When VirtioScsiPassThru() is failed due to some reasons then this function
388 can be called to construct a host adapter error.
390 @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host
391 adapter error shall be placed in.
394 @retval EFI_DEVICE_ERROR The function returns this status code
395 unconditionally, to be propagated by
396 VirtioScsiPassThru().
401 ReportHostAdapterError (
402 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
405 Packet
->InTransferLength
= 0;
406 Packet
->OutTransferLength
= 0;
407 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
408 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
409 Packet
->SenseDataLength
= 0;
410 return EFI_DEVICE_ERROR
;
414 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
415 // for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
416 // - 14.1 SCSI Driver Model Overview,
417 // - 14.7 Extended SCSI Pass Thru Protocol.
423 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
426 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
427 IN EFI_EVENT Event OPTIONAL
433 volatile VIRTIO_SCSI_REQ Request
;
434 volatile VIRTIO_SCSI_RESP
*Response
;
435 VOID
*ResponseBuffer
;
436 DESC_INDICES Indices
;
437 VOID
*RequestMapping
;
438 VOID
*ResponseMapping
;
440 VOID
*OutDataMapping
;
441 EFI_PHYSICAL_ADDRESS RequestDeviceAddress
;
442 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress
;
443 EFI_PHYSICAL_ADDRESS InDataDeviceAddress
;
444 EFI_PHYSICAL_ADDRESS OutDataDeviceAddress
;
446 UINTN InDataNumPages
;
447 BOOLEAN OutDataBufferIsMapped
;
450 // Set InDataMapping,OutDataMapping,InDataDeviceAddress and OutDataDeviceAddress to
451 // suppress incorrect compiler/analyzer warnings.
453 InDataMapping
= NULL
;
454 OutDataMapping
= NULL
;
455 InDataDeviceAddress
= 0;
456 OutDataDeviceAddress
= 0;
458 ZeroMem ((VOID
*)&Request
, sizeof (Request
));
460 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
461 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
464 OutDataBufferIsMapped
= FALSE
;
467 Status
= PopulateRequest (Dev
, TargetValue
, Lun
, Packet
, &Request
);
468 if (EFI_ERROR (Status
)) {
473 // Map the virtio-scsi Request header buffer
475 Status
= VirtioMapAllBytesInSharedBuffer (
477 VirtioOperationBusMasterRead
,
480 &RequestDeviceAddress
,
483 if (EFI_ERROR (Status
)) {
484 return ReportHostAdapterError (Packet
);
488 // Map the input buffer
490 if (Packet
->InTransferLength
> 0) {
492 // Allocate a intermediate input buffer. This is mainly to handle the
494 // * caller submits a bi-directional request
495 // * we perform the request fine
496 // * but we fail to unmap the "InDataMapping"
498 // In that case simply returning the EFI_DEVICE_ERROR is not sufficient. In
499 // addition to the error code we also need to update Packet fields
500 // accordingly so that we report the full loss of the incoming transfer.
502 // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If
503 // the Virtio request is successful then we copy the data from temporary
504 // buffer into Packet->InDataBuffer.
506 InDataNumPages
= EFI_SIZE_TO_PAGES ((UINTN
)Packet
->InTransferLength
);
507 Status
= Dev
->VirtIo
->AllocateSharedPages (
512 if (EFI_ERROR (Status
)) {
513 Status
= ReportHostAdapterError (Packet
);
514 goto UnmapRequestBuffer
;
517 ZeroMem (InDataBuffer
, Packet
->InTransferLength
);
519 Status
= VirtioMapAllBytesInSharedBuffer (
521 VirtioOperationBusMasterCommonBuffer
,
523 Packet
->InTransferLength
,
524 &InDataDeviceAddress
,
527 if (EFI_ERROR (Status
)) {
528 Status
= ReportHostAdapterError (Packet
);
529 goto FreeInDataBuffer
;
534 // Map the output buffer
536 if (Packet
->OutTransferLength
> 0) {
537 Status
= VirtioMapAllBytesInSharedBuffer (
539 VirtioOperationBusMasterRead
,
540 Packet
->OutDataBuffer
,
541 Packet
->OutTransferLength
,
542 &OutDataDeviceAddress
,
545 if (EFI_ERROR (Status
)) {
546 Status
= ReportHostAdapterError (Packet
);
547 goto UnmapInDataBuffer
;
550 OutDataBufferIsMapped
= TRUE
;
554 // Response header is bi-direction (we preset with host status and expect
555 // the device to update it). Allocate a response buffer which can be mapped
556 // to access equally by both processor and device.
558 Status
= Dev
->VirtIo
->AllocateSharedPages (
560 EFI_SIZE_TO_PAGES (sizeof *Response
),
563 if (EFI_ERROR (Status
)) {
564 Status
= ReportHostAdapterError (Packet
);
565 goto UnmapOutDataBuffer
;
568 Response
= ResponseBuffer
;
570 ZeroMem ((VOID
*)Response
, sizeof (*Response
));
573 // preset a host status for ourselves that we do not accept as success
575 Response
->Response
= VIRTIO_SCSI_S_FAILURE
;
578 // Map the response buffer with BusMasterCommonBuffer so that response
579 // buffer can be accessed by both host and device.
581 Status
= VirtioMapAllBytesInSharedBuffer (
583 VirtioOperationBusMasterCommonBuffer
,
586 &ResponseDeviceAddress
,
589 if (EFI_ERROR (Status
)) {
590 Status
= ReportHostAdapterError (Packet
);
591 goto FreeResponseBuffer
;
594 VirtioPrepare (&Dev
->Ring
, &Indices
);
597 // ensured by VirtioScsiInit() -- this predicate, in combination with the
598 // lock-step progress, ensures we don't have to track free descriptors.
600 ASSERT (Dev
->Ring
.QueueSize
>= 4);
607 RequestDeviceAddress
,
614 // enqueue "dataout" if any
616 if (Packet
->OutTransferLength
> 0) {
619 OutDataDeviceAddress
,
620 Packet
->OutTransferLength
,
627 // enqueue Response, to be written by the host
631 ResponseDeviceAddress
,
633 VRING_DESC_F_WRITE
| (Packet
->InTransferLength
> 0 ? VRING_DESC_F_NEXT
: 0),
638 // enqueue "datain" if any, to be written by the host
640 if (Packet
->InTransferLength
> 0) {
644 Packet
->InTransferLength
,
650 // If kicking the host fails, we must fake a host adapter error.
651 // EFI_NOT_READY would save us the effort, but it would also suggest that the
656 VIRTIO_SCSI_REQUEST_QUEUE
,
662 Status
= ReportHostAdapterError (Packet
);
663 goto UnmapResponseBuffer
;
666 Status
= ParseResponse (Packet
, Response
);
669 // If virtio request was successful and it was a CPU read request then we
670 // have used an intermediate buffer. Copy the data from intermediate buffer
671 // to the final buffer.
673 if (InDataBuffer
!= NULL
) {
674 CopyMem (Packet
->InDataBuffer
, InDataBuffer
, Packet
->InTransferLength
);
678 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, ResponseMapping
);
681 Dev
->VirtIo
->FreeSharedPages (
683 EFI_SIZE_TO_PAGES (sizeof *Response
),
688 if (OutDataBufferIsMapped
) {
689 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, OutDataMapping
);
693 if (InDataBuffer
!= NULL
) {
694 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, InDataMapping
);
698 if (InDataBuffer
!= NULL
) {
699 Dev
->VirtIo
->FreeSharedPages (Dev
->VirtIo
, InDataNumPages
, InDataBuffer
);
703 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, RequestMapping
);
710 VirtioScsiGetNextTargetLun (
711 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
712 IN OUT UINT8
**TargetPointer
,
722 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
724 Target
= *TargetPointer
;
727 // Search for first non-0xFF byte. If not found, return first target & LUN.
729 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
) {
732 if (Idx
== TARGET_MAX_BYTES
) {
733 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
739 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
741 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
744 // increment (target, LUN) pair if valid on input
746 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
747 if ((LastTarget
> Dev
->MaxTarget
) || (*Lun
> Dev
->MaxLun
)) {
748 return EFI_INVALID_PARAMETER
;
751 if (*Lun
< Dev
->MaxLun
) {
756 if (LastTarget
< Dev
->MaxTarget
) {
759 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
763 return EFI_NOT_FOUND
;
768 VirtioScsiBuildDevicePath (
769 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
772 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
777 SCSI_DEVICE_PATH
*ScsiDevicePath
;
779 if (DevicePath
== NULL
) {
780 return EFI_INVALID_PARAMETER
;
783 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
784 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
785 if ((TargetValue
> Dev
->MaxTarget
) || (Lun
> Dev
->MaxLun
) || (Lun
> 0xFFFF)) {
786 return EFI_NOT_FOUND
;
789 ScsiDevicePath
= AllocatePool (sizeof *ScsiDevicePath
);
790 if (ScsiDevicePath
== NULL
) {
791 return EFI_OUT_OF_RESOURCES
;
794 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
795 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
796 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof *ScsiDevicePath
;
797 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof *ScsiDevicePath
>> 8);
798 ScsiDevicePath
->Pun
= TargetValue
;
799 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
801 *DevicePath
= &ScsiDevicePath
->Header
;
807 VirtioScsiGetTargetLun (
808 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
809 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
810 OUT UINT8
**TargetPointer
,
814 SCSI_DEVICE_PATH
*ScsiDevicePath
;
818 if ((DevicePath
== NULL
) || (TargetPointer
== NULL
) || (*TargetPointer
== NULL
) ||
821 return EFI_INVALID_PARAMETER
;
824 if ((DevicePath
->Type
!= MESSAGING_DEVICE_PATH
) ||
825 (DevicePath
->SubType
!= MSG_SCSI_DP
))
827 return EFI_UNSUPPORTED
;
830 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
831 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
832 if ((ScsiDevicePath
->Pun
> Dev
->MaxTarget
) ||
833 (ScsiDevicePath
->Lun
> Dev
->MaxLun
))
835 return EFI_NOT_FOUND
;
839 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
840 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
841 // c) ScsiDevicePath->Pun is an UINT16
843 Target
= *TargetPointer
;
844 CopyMem (Target
, &ScsiDevicePath
->Pun
, 2);
845 SetMem (Target
+ 2, TARGET_MAX_BYTES
- 2, 0x00);
847 *Lun
= ScsiDevicePath
->Lun
;
853 VirtioScsiResetChannel (
854 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
857 return EFI_UNSUPPORTED
;
862 VirtioScsiResetTargetLun (
863 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
868 return EFI_UNSUPPORTED
;
873 VirtioScsiGetNextTarget (
874 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
875 IN OUT UINT8
**TargetPointer
884 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
886 Target
= *TargetPointer
;
889 // Search for first non-0xFF byte. If not found, return first target.
891 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
) {
894 if (Idx
== TARGET_MAX_BYTES
) {
895 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
900 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
902 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
905 // increment target if valid on input
907 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
908 if (LastTarget
> Dev
->MaxTarget
) {
909 return EFI_INVALID_PARAMETER
;
912 if (LastTarget
< Dev
->MaxTarget
) {
914 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
918 return EFI_NOT_FOUND
;
925 IN OUT VSCSI_DEV
*Dev
930 UINT64 RingBaseShift
;
932 UINT16 MaxChannel
; // for validation only
933 UINT32 NumQueues
; // for validation only
937 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
939 NextDevStat
= 0; // step 1 -- reset device
940 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
941 if (EFI_ERROR (Status
)) {
945 NextDevStat
|= VSTAT_ACK
; // step 2 -- acknowledge device presence
946 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
947 if (EFI_ERROR (Status
)) {
951 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
952 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
953 if (EFI_ERROR (Status
)) {
958 // Set Page Size - MMIO VirtIo Specific
960 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
961 if (EFI_ERROR (Status
)) {
966 // step 4a -- retrieve and validate features
968 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
969 if (EFI_ERROR (Status
)) {
973 Dev
->InOutSupported
= (BOOLEAN
)((Features
& VIRTIO_SCSI_F_INOUT
) != 0);
975 Status
= VIRTIO_CFG_READ (Dev
, MaxChannel
, &MaxChannel
);
976 if (EFI_ERROR (Status
)) {
980 if (MaxChannel
!= 0) {
982 // this driver is for a single-channel virtio-scsi HBA
984 Status
= EFI_UNSUPPORTED
;
988 Status
= VIRTIO_CFG_READ (Dev
, NumQueues
, &NumQueues
);
989 if (EFI_ERROR (Status
)) {
994 Status
= EFI_UNSUPPORTED
;
998 Status
= VIRTIO_CFG_READ (Dev
, MaxTarget
, &Dev
->MaxTarget
);
999 if (EFI_ERROR (Status
)) {
1003 if (Dev
->MaxTarget
> PcdGet16 (PcdVirtioScsiMaxTargetLimit
)) {
1004 Dev
->MaxTarget
= PcdGet16 (PcdVirtioScsiMaxTargetLimit
);
1007 Status
= VIRTIO_CFG_READ (Dev
, MaxLun
, &Dev
->MaxLun
);
1008 if (EFI_ERROR (Status
)) {
1012 if (Dev
->MaxLun
> PcdGet32 (PcdVirtioScsiMaxLunLimit
)) {
1013 Dev
->MaxLun
= PcdGet32 (PcdVirtioScsiMaxLunLimit
);
1016 Status
= VIRTIO_CFG_READ (Dev
, MaxSectors
, &Dev
->MaxSectors
);
1017 if (EFI_ERROR (Status
)) {
1021 if (Dev
->MaxSectors
< 2) {
1023 // We must be able to halve it for bidirectional transfers
1024 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
1026 Status
= EFI_UNSUPPORTED
;
1030 Features
&= VIRTIO_SCSI_F_INOUT
| VIRTIO_F_VERSION_1
|
1031 VIRTIO_F_IOMMU_PLATFORM
;
1034 // In virtio-1.0, feature negotiation is expected to complete before queue
1035 // discovery, and the device can also reject the selected set of features.
1037 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
1038 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
1039 if (EFI_ERROR (Status
)) {
1045 // step 4b -- allocate request virtqueue
1047 Status
= Dev
->VirtIo
->SetQueueSel (Dev
->VirtIo
, VIRTIO_SCSI_REQUEST_QUEUE
);
1048 if (EFI_ERROR (Status
)) {
1052 Status
= Dev
->VirtIo
->GetQueueNumMax (Dev
->VirtIo
, &QueueSize
);
1053 if (EFI_ERROR (Status
)) {
1058 // VirtioScsiPassThru() uses at most four descriptors
1060 if (QueueSize
< 4) {
1061 Status
= EFI_UNSUPPORTED
;
1065 Status
= VirtioRingInit (Dev
->VirtIo
, QueueSize
, &Dev
->Ring
);
1066 if (EFI_ERROR (Status
)) {
1071 // If anything fails from here on, we must release the ring resources
1073 Status
= VirtioRingMap (
1079 if (EFI_ERROR (Status
)) {
1084 // Additional steps for MMIO: align the queue appropriately, and set the
1085 // size. If anything fails from here on, we must unmap the ring resources.
1087 Status
= Dev
->VirtIo
->SetQueueNum (Dev
->VirtIo
, QueueSize
);
1088 if (EFI_ERROR (Status
)) {
1092 Status
= Dev
->VirtIo
->SetQueueAlign (Dev
->VirtIo
, EFI_PAGE_SIZE
);
1093 if (EFI_ERROR (Status
)) {
1098 // step 4c -- Report GPFN (guest-physical frame number) of queue.
1100 Status
= Dev
->VirtIo
->SetQueueAddress (
1105 if (EFI_ERROR (Status
)) {
1110 // step 5 -- Report understood features and guest-tuneables.
1112 if (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
1113 Features
&= ~(UINT64
)(VIRTIO_F_VERSION_1
| VIRTIO_F_IOMMU_PLATFORM
);
1114 Status
= Dev
->VirtIo
->SetGuestFeatures (Dev
->VirtIo
, Features
);
1115 if (EFI_ERROR (Status
)) {
1121 // We expect these maximum sizes from the host. Since they are
1122 // guest-negotiable, ask for them rather than just checking them.
1124 Status
= VIRTIO_CFG_WRITE (Dev
, CdbSize
, VIRTIO_SCSI_CDB_SIZE
);
1125 if (EFI_ERROR (Status
)) {
1129 Status
= VIRTIO_CFG_WRITE (Dev
, SenseSize
, VIRTIO_SCSI_SENSE_SIZE
);
1130 if (EFI_ERROR (Status
)) {
1135 // step 6 -- initialization complete
1137 NextDevStat
|= VSTAT_DRIVER_OK
;
1138 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
1139 if (EFI_ERROR (Status
)) {
1144 // populate the exported interface's attributes
1146 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1147 Dev
->PassThru
.PassThru
= &VirtioScsiPassThru
;
1148 Dev
->PassThru
.GetNextTargetLun
= &VirtioScsiGetNextTargetLun
;
1149 Dev
->PassThru
.BuildDevicePath
= &VirtioScsiBuildDevicePath
;
1150 Dev
->PassThru
.GetTargetLun
= &VirtioScsiGetTargetLun
;
1151 Dev
->PassThru
.ResetChannel
= &VirtioScsiResetChannel
;
1152 Dev
->PassThru
.ResetTargetLun
= &VirtioScsiResetTargetLun
;
1153 Dev
->PassThru
.GetNextTarget
= &VirtioScsiGetNextTarget
;
1156 // AdapterId is a target for which no handle will be created during bus scan.
1157 // Prevent any conflict with real devices.
1159 Dev
->PassThruMode
.AdapterId
= 0xFFFFFFFF;
1162 // Set both physical and logical attributes for non-RAID SCSI channel. See
1163 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
1164 // SCSI Pass Thru Protocol.
1166 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1167 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1170 // no restriction on transfer buffer alignment
1172 Dev
->PassThruMode
.IoAlign
= 0;
1177 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RingMap
);
1180 VirtioRingUninit (Dev
->VirtIo
, &Dev
->Ring
);
1184 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
1185 // Status. VirtIo access failure here should not mask the original error.
1187 NextDevStat
|= VSTAT_FAILED
;
1188 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
1190 Dev
->InOutSupported
= FALSE
;
1193 Dev
->MaxSectors
= 0;
1195 return Status
; // reached only via Failed above
1202 IN OUT VSCSI_DEV
*Dev
1206 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
1207 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
1208 // the old comms area.
1210 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
1212 Dev
->InOutSupported
= FALSE
;
1215 Dev
->MaxSectors
= 0;
1217 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RingMap
);
1218 VirtioRingUninit (Dev
->VirtIo
, &Dev
->Ring
);
1220 SetMem (&Dev
->PassThru
, sizeof Dev
->PassThru
, 0x00);
1221 SetMem (&Dev
->PassThruMode
, sizeof Dev
->PassThruMode
, 0x00);
1225 // Event notification function enqueued by ExitBootServices().
1231 VirtioScsiExitBoot (
1238 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1240 // Reset the device. This causes the hypervisor to forget about the virtio
1243 // We allocated said ring in EfiBootServicesData type memory, and code
1244 // executing after ExitBootServices() is permitted to overwrite it.
1247 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
1251 // Probe, start and stop functions of this driver, called by the DXE core for
1252 // specific devices.
1254 // The following specifications document these interfaces:
1255 // - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
1256 // - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
1258 // The implementation follows:
1259 // - Driver Writer's Guide for UEFI 2.3.1 v1.01
1260 // - 5.1.3.4 OpenProtocol() and CloseProtocol()
1261 // - UEFI Spec 2.3.1 + Errata C
1262 // - 6.3 Protocol Handler Services
1267 VirtioScsiDriverBindingSupported (
1268 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1269 IN EFI_HANDLE DeviceHandle
,
1270 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1274 VIRTIO_DEVICE_PROTOCOL
*VirtIo
;
1277 // Attempt to open the device with the VirtIo set of interfaces. On success,
1278 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
1279 // attempts (EFI_ALREADY_STARTED).
1281 Status
= gBS
->OpenProtocol (
1282 DeviceHandle
, // candidate device
1283 &gVirtioDeviceProtocolGuid
, // for generic VirtIo access
1284 (VOID
**)&VirtIo
, // handle to instantiate
1285 This
->DriverBindingHandle
, // requestor driver identity
1286 DeviceHandle
, // ControllerHandle, according to
1287 // the UEFI Driver Model
1288 EFI_OPEN_PROTOCOL_BY_DRIVER
// get exclusive VirtIo access to
1289 // the device; to be released
1291 if (EFI_ERROR (Status
)) {
1295 if (VirtIo
->SubSystemDeviceId
!= VIRTIO_SUBSYSTEM_SCSI_HOST
) {
1296 Status
= EFI_UNSUPPORTED
;
1300 // We needed VirtIo access only transitorily, to see whether we support the
1303 gBS
->CloseProtocol (
1305 &gVirtioDeviceProtocolGuid
,
1306 This
->DriverBindingHandle
,
1314 VirtioScsiDriverBindingStart (
1315 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1316 IN EFI_HANDLE DeviceHandle
,
1317 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1323 Dev
= (VSCSI_DEV
*)AllocateZeroPool (sizeof *Dev
);
1325 return EFI_OUT_OF_RESOURCES
;
1328 Status
= gBS
->OpenProtocol (
1330 &gVirtioDeviceProtocolGuid
,
1331 (VOID
**)&Dev
->VirtIo
,
1332 This
->DriverBindingHandle
,
1334 EFI_OPEN_PROTOCOL_BY_DRIVER
1336 if (EFI_ERROR (Status
)) {
1337 goto FreeVirtioScsi
;
1341 // VirtIo access granted, configure virtio-scsi device.
1343 Status
= VirtioScsiInit (Dev
);
1344 if (EFI_ERROR (Status
)) {
1348 Status
= gBS
->CreateEvent (
1349 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
1351 &VirtioScsiExitBoot
,
1355 if (EFI_ERROR (Status
)) {
1360 // Setup complete, attempt to export the driver instance's PassThru
1363 Dev
->Signature
= VSCSI_SIG
;
1364 Status
= gBS
->InstallProtocolInterface (
1366 &gEfiExtScsiPassThruProtocolGuid
,
1367 EFI_NATIVE_INTERFACE
,
1370 if (EFI_ERROR (Status
)) {
1377 gBS
->CloseEvent (Dev
->ExitBoot
);
1380 VirtioScsiUninit (Dev
);
1383 gBS
->CloseProtocol (
1385 &gVirtioDeviceProtocolGuid
,
1386 This
->DriverBindingHandle
,
1398 VirtioScsiDriverBindingStop (
1399 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1400 IN EFI_HANDLE DeviceHandle
,
1401 IN UINTN NumberOfChildren
,
1402 IN EFI_HANDLE
*ChildHandleBuffer
1406 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1409 Status
= gBS
->OpenProtocol (
1410 DeviceHandle
, // candidate device
1411 &gEfiExtScsiPassThruProtocolGuid
, // retrieve the SCSI iface
1412 (VOID
**)&PassThru
, // target pointer
1413 This
->DriverBindingHandle
, // requestor driver ident.
1414 DeviceHandle
, // lookup req. for dev.
1415 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// lookup only, no new ref.
1417 if (EFI_ERROR (Status
)) {
1421 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (PassThru
);
1424 // Handle Stop() requests for in-use driver instances gracefully.
1426 Status
= gBS
->UninstallProtocolInterface (
1428 &gEfiExtScsiPassThruProtocolGuid
,
1431 if (EFI_ERROR (Status
)) {
1435 gBS
->CloseEvent (Dev
->ExitBoot
);
1437 VirtioScsiUninit (Dev
);
1439 gBS
->CloseProtocol (
1441 &gVirtioDeviceProtocolGuid
,
1442 This
->DriverBindingHandle
,
1452 // The static object that groups the Supported() (ie. probe), Start() and
1453 // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1454 // C, 10.1 EFI Driver Binding Protocol.
1456 STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding
= {
1457 &VirtioScsiDriverBindingSupported
,
1458 &VirtioScsiDriverBindingStart
,
1459 &VirtioScsiDriverBindingStop
,
1460 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1461 NULL
, // ImageHandle, to be overwritten by
1462 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
1463 NULL
// DriverBindingHandle, ditto
1467 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1468 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1469 // in English, for display on standard console devices. This is recommended for
1470 // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1471 // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1473 // Device type names ("Virtio SCSI Host Device") are not formatted because the
1474 // driver supports only that device type. Therefore the driver name suffices
1475 // for unambiguous identification.
1479 EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1480 { "eng;en", L
"Virtio SCSI Host Driver" },
1485 EFI_COMPONENT_NAME_PROTOCOL gComponentName
;
1489 VirtioScsiGetDriverName (
1490 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1492 OUT CHAR16
**DriverName
1495 return LookupUnicodeString2 (
1497 This
->SupportedLanguages
,
1500 (BOOLEAN
)(This
== &gComponentName
) // Iso639Language
1506 VirtioScsiGetDeviceName (
1507 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1508 IN EFI_HANDLE DeviceHandle
,
1509 IN EFI_HANDLE ChildHandle
,
1511 OUT CHAR16
**ControllerName
1514 return EFI_UNSUPPORTED
;
1518 EFI_COMPONENT_NAME_PROTOCOL gComponentName
= {
1519 &VirtioScsiGetDriverName
,
1520 &VirtioScsiGetDeviceName
,
1521 "eng" // SupportedLanguages, ISO 639-2 language codes
1525 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2
= {
1526 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
)&VirtioScsiGetDriverName
,
1527 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
)&VirtioScsiGetDeviceName
,
1528 "en" // SupportedLanguages, RFC 4646 language codes
1532 // Entry point of this driver.
1536 VirtioScsiEntryPoint (
1537 IN EFI_HANDLE ImageHandle
,
1538 IN EFI_SYSTEM_TABLE
*SystemTable
1541 return EfiLibInstallDriverBindingComponentName2 (