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), \
89 // UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies
90 // the PassThru() interface. Beside returning a status code, the function must
91 // set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out
92 // parameter on return. The following is a full list of those fields, for
93 // easier validation of PopulateRequest(), ParseResponse(), and
94 // ReportHostAdapterError() below.
97 // - OutTransferLength
98 // - HostAdapterStatus
103 // On any return from the PassThru() interface, these fields must be set,
104 // except if the returned status code is explicitly exempt. (Actually the
105 // implementation here conservatively sets these fields even in case not all
106 // of them would be required by the specification.)
111 Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol
114 The caller is responsible for pre-zeroing the virtio-scsi request. The
115 Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards
116 by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.
118 @param[in] Dev The virtio-scsi host device the packet targets.
120 @param[in] Target The SCSI target controlled by the virtio-scsi host
123 @param[in] Lun The Logical Unit Number under the SCSI target.
125 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the
126 function translates to a virtio-scsi request. On
127 failure this parameter relays error contents.
129 @param[out] Request The pre-zeroed virtio-scsi request to populate. This
130 parameter is volatile-qualified because we expect the
131 caller to append it to a virtio ring, thus
132 assignments to Request must be visible when the
136 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,
137 Request has been populated.
139 @return Otherwise, invalid or unsupported parameters were
140 detected. Status codes are meant for direct forwarding
141 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
149 IN CONST VSCSI_DEV
*Dev
,
152 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
153 OUT
volatile VIRTIO_SCSI_REQ
*Request
160 // bidirectional transfer was requested, but the host doesn't support it
162 (Packet
->InTransferLength
> 0 && Packet
->OutTransferLength
> 0 &&
163 !Dev
->InOutSupported
) ||
166 // a target / LUN was addressed that's impossible to encode for the host
168 Target
> 0xFF || Lun
>= 0x4000 ||
171 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE
173 Packet
->CdbLength
> VIRTIO_SCSI_CDB_SIZE
||
176 // From virtio-0.9.5, 2.3.2 Descriptor Table:
177 // "no descriptor chain may be more than 2^32 bytes long in total".
179 (UINT64
) Packet
->InTransferLength
+ Packet
->OutTransferLength
> SIZE_1GB
183 // this error code doesn't require updates to the Packet output fields
185 return EFI_UNSUPPORTED
;
190 // addressed invalid device
192 Target
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
||
195 // invalid direction (there doesn't seem to be a macro for the "no data
196 // transferred" "direction", eg. for TEST UNIT READY)
198 Packet
->DataDirection
> EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
||
201 // trying to receive, but destination pointer is NULL, or contradicting
202 // transfer direction
204 (Packet
->InTransferLength
> 0 &&
205 (Packet
->InDataBuffer
== NULL
||
206 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_WRITE
211 // trying to send, but source pointer is NULL, or contradicting transfer
214 (Packet
->OutTransferLength
> 0 &&
215 (Packet
->OutDataBuffer
== NULL
||
216 Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
222 // this error code doesn't require updates to the Packet output fields
224 return EFI_INVALID_PARAMETER
;
228 // Catch oversized requests eagerly. If this condition evaluates to false,
229 // then the combined size of a bidirectional request will not exceed the
230 // virtio-scsi device's transfer limit either.
232 if (ALIGN_VALUE (Packet
->OutTransferLength
, 512) / 512
233 > Dev
->MaxSectors
/ 2 ||
234 ALIGN_VALUE (Packet
->InTransferLength
, 512) / 512
235 > 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
];
268 Parse the virtio-scsi device's response, translate it to an EFI status code,
269 and update the Extended SCSI Pass Thru Protocol packet, to be returned by
270 the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.
272 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has
273 been translated to a virtio-scsi request with
274 PopulateRequest(), and processed by the host. On
275 output this parameter is updated with response or
278 @param[in] Response The virtio-scsi response structure to parse. We expect
279 it to come from a virtio ring, thus it is qualified
283 @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7
284 Extended SCSI Pass Thru Protocol.
291 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
292 IN CONST
volatile VIRTIO_SCSI_RESP
*Response
295 UINTN ResponseSenseLen
;
299 // return sense data (length and contents) in all cases, truncated if needed
301 ResponseSenseLen
= MIN (Response
->SenseLen
, VIRTIO_SCSI_SENSE_SIZE
);
302 if (Packet
->SenseDataLength
> ResponseSenseLen
) {
303 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
;
327 Packet
->OutTransferLength
-= Response
->Residual
- Packet
->InTransferLength
;
328 Packet
->InTransferLength
= 0;
332 // report target status in all cases
334 Packet
->TargetStatus
= Response
->Status
;
337 // host adapter status and function return value depend on virtio-scsi
340 switch (Response
->Response
) {
341 case VIRTIO_SCSI_S_OK
:
342 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
345 case VIRTIO_SCSI_S_OVERRUN
:
346 Packet
->HostAdapterStatus
=
347 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
350 case VIRTIO_SCSI_S_BAD_TARGET
:
352 // This is non-intuitive but explicitly required by the
353 // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for
354 // disconnected (but otherwise valid) target / LUN addresses.
356 Packet
->HostAdapterStatus
=
357 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND
;
360 case VIRTIO_SCSI_S_RESET
:
361 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
364 case VIRTIO_SCSI_S_BUSY
:
365 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
366 return EFI_NOT_READY
;
369 // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is
370 // intentional as well, not an oversight.
372 case VIRTIO_SCSI_S_ABORTED
:
373 case VIRTIO_SCSI_S_TRANSPORT_FAILURE
:
374 case VIRTIO_SCSI_S_TARGET_FAILURE
:
375 case VIRTIO_SCSI_S_NEXUS_FAILURE
:
376 case VIRTIO_SCSI_S_FAILURE
:
378 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
381 return EFI_DEVICE_ERROR
;
387 The function can be used to create a fake host adapter error.
389 When VirtioScsiPassThru() is failed due to some reasons then this function
390 can be called to construct a host adapter error.
392 @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host
393 adapter error shall be placed in.
396 @retval EFI_DEVICE_ERROR The function returns this status code
397 unconditionally, to be propagated by
398 VirtioScsiPassThru().
403 ReportHostAdapterError (
404 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
407 Packet
->InTransferLength
= 0;
408 Packet
->OutTransferLength
= 0;
409 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
410 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
411 Packet
->SenseDataLength
= 0;
412 return EFI_DEVICE_ERROR
;
417 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
418 // for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
419 // - 14.1 SCSI Driver Model Overview,
420 // - 14.7 Extended SCSI Pass Thru Protocol.
426 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
429 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
430 IN EFI_EVENT Event OPTIONAL
436 volatile VIRTIO_SCSI_REQ Request
;
437 volatile VIRTIO_SCSI_RESP
*Response
;
438 VOID
*ResponseBuffer
;
439 DESC_INDICES Indices
;
440 VOID
*RequestMapping
;
441 VOID
*ResponseMapping
;
443 VOID
*OutDataMapping
;
444 EFI_PHYSICAL_ADDRESS RequestDeviceAddress
;
445 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress
;
446 EFI_PHYSICAL_ADDRESS InDataDeviceAddress
;
447 EFI_PHYSICAL_ADDRESS OutDataDeviceAddress
;
449 UINTN InDataNumPages
;
450 BOOLEAN OutDataBufferIsMapped
;
453 // Set InDataMapping,OutDataMapping,InDataDeviceAddress and OutDataDeviceAddress to
454 // suppress incorrect compiler/analyzer warnings.
456 InDataMapping
= NULL
;
457 OutDataMapping
= NULL
;
458 InDataDeviceAddress
= 0;
459 OutDataDeviceAddress
= 0;
461 ZeroMem ((VOID
*) &Request
, sizeof (Request
));
463 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
464 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
467 OutDataBufferIsMapped
= FALSE
;
470 Status
= PopulateRequest (Dev
, TargetValue
, Lun
, Packet
, &Request
);
471 if (EFI_ERROR (Status
)) {
476 // Map the virtio-scsi Request header buffer
478 Status
= VirtioMapAllBytesInSharedBuffer (
480 VirtioOperationBusMasterRead
,
483 &RequestDeviceAddress
,
485 if (EFI_ERROR (Status
)) {
486 return ReportHostAdapterError (Packet
);
490 // Map the input buffer
492 if (Packet
->InTransferLength
> 0) {
494 // Allocate a intermediate input buffer. This is mainly to handle the
496 // * caller submits a bi-directional request
497 // * we perform the request fine
498 // * but we fail to unmap the "InDataMapping"
500 // In that case simply returning the EFI_DEVICE_ERROR is not sufficient. In
501 // addition to the error code we also need to update Packet fields
502 // accordingly so that we report the full loss of the incoming transfer.
504 // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If
505 // the Virtio request is successful then we copy the data from temporary
506 // buffer into Packet->InDataBuffer.
508 InDataNumPages
= EFI_SIZE_TO_PAGES ((UINTN
)Packet
->InTransferLength
);
509 Status
= Dev
->VirtIo
->AllocateSharedPages (
514 if (EFI_ERROR (Status
)) {
515 Status
= ReportHostAdapterError (Packet
);
516 goto UnmapRequestBuffer
;
519 ZeroMem (InDataBuffer
, Packet
->InTransferLength
);
521 Status
= VirtioMapAllBytesInSharedBuffer (
523 VirtioOperationBusMasterCommonBuffer
,
525 Packet
->InTransferLength
,
526 &InDataDeviceAddress
,
529 if (EFI_ERROR (Status
)) {
530 Status
= ReportHostAdapterError (Packet
);
531 goto FreeInDataBuffer
;
536 // Map the output buffer
538 if (Packet
->OutTransferLength
> 0) {
539 Status
= VirtioMapAllBytesInSharedBuffer (
541 VirtioOperationBusMasterRead
,
542 Packet
->OutDataBuffer
,
543 Packet
->OutTransferLength
,
544 &OutDataDeviceAddress
,
547 if (EFI_ERROR (Status
)) {
548 Status
= ReportHostAdapterError (Packet
);
549 goto UnmapInDataBuffer
;
552 OutDataBufferIsMapped
= TRUE
;
556 // Response header is bi-direction (we preset with host status and expect
557 // the device to update it). Allocate a response buffer which can be mapped
558 // to access equally by both processor and device.
560 Status
= Dev
->VirtIo
->AllocateSharedPages (
562 EFI_SIZE_TO_PAGES (sizeof *Response
),
565 if (EFI_ERROR (Status
)) {
566 Status
= ReportHostAdapterError (Packet
);
567 goto UnmapOutDataBuffer
;
570 Response
= ResponseBuffer
;
572 ZeroMem ((VOID
*)Response
, sizeof (*Response
));
575 // preset a host status for ourselves that we do not accept as success
577 Response
->Response
= VIRTIO_SCSI_S_FAILURE
;
580 // Map the response buffer with BusMasterCommonBuffer so that response
581 // buffer can be accessed by both host and device.
583 Status
= VirtioMapAllBytesInSharedBuffer (
585 VirtioOperationBusMasterCommonBuffer
,
588 &ResponseDeviceAddress
,
591 if (EFI_ERROR (Status
)) {
592 Status
= ReportHostAdapterError (Packet
);
593 goto FreeResponseBuffer
;
596 VirtioPrepare (&Dev
->Ring
, &Indices
);
599 // ensured by VirtioScsiInit() -- this predicate, in combination with the
600 // lock-step progress, ensures we don't have to track free descriptors.
602 ASSERT (Dev
->Ring
.QueueSize
>= 4);
609 RequestDeviceAddress
,
616 // enqueue "dataout" if any
618 if (Packet
->OutTransferLength
> 0) {
621 OutDataDeviceAddress
,
622 Packet
->OutTransferLength
,
629 // enqueue Response, to be written by the host
633 ResponseDeviceAddress
,
635 VRING_DESC_F_WRITE
| (Packet
->InTransferLength
> 0 ? VRING_DESC_F_NEXT
: 0),
640 // enqueue "datain" if any, to be written by the host
642 if (Packet
->InTransferLength
> 0) {
646 Packet
->InTransferLength
,
652 // If kicking the host fails, we must fake a host adapter error.
653 // EFI_NOT_READY would save us the effort, but it would also suggest that the
656 if (VirtioFlush (Dev
->VirtIo
, VIRTIO_SCSI_REQUEST_QUEUE
, &Dev
->Ring
,
657 &Indices
, NULL
) != EFI_SUCCESS
) {
658 Status
= ReportHostAdapterError (Packet
);
659 goto UnmapResponseBuffer
;
662 Status
= ParseResponse (Packet
, Response
);
665 // If virtio request was successful and it was a CPU read request then we
666 // have used an intermediate buffer. Copy the data from intermediate buffer
667 // to the final buffer.
669 if (InDataBuffer
!= NULL
) {
670 CopyMem (Packet
->InDataBuffer
, InDataBuffer
, Packet
->InTransferLength
);
674 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, ResponseMapping
);
677 Dev
->VirtIo
->FreeSharedPages (
679 EFI_SIZE_TO_PAGES (sizeof *Response
),
684 if (OutDataBufferIsMapped
) {
685 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, OutDataMapping
);
689 if (InDataBuffer
!= NULL
) {
690 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, InDataMapping
);
694 if (InDataBuffer
!= NULL
) {
695 Dev
->VirtIo
->FreeSharedPages (Dev
->VirtIo
, InDataNumPages
, InDataBuffer
);
699 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, RequestMapping
);
707 VirtioScsiGetNextTargetLun (
708 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
709 IN OUT UINT8
**TargetPointer
,
719 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
721 Target
= *TargetPointer
;
724 // Search for first non-0xFF byte. If not found, return first target & LUN.
726 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
728 if (Idx
== TARGET_MAX_BYTES
) {
729 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
735 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
737 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
740 // increment (target, LUN) pair if valid on input
742 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
743 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
744 return EFI_INVALID_PARAMETER
;
747 if (*Lun
< Dev
->MaxLun
) {
752 if (LastTarget
< Dev
->MaxTarget
) {
755 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
759 return EFI_NOT_FOUND
;
765 VirtioScsiBuildDevicePath (
766 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
769 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
774 SCSI_DEVICE_PATH
*ScsiDevicePath
;
776 if (DevicePath
== NULL
) {
777 return EFI_INVALID_PARAMETER
;
780 CopyMem (&TargetValue
, Target
, sizeof TargetValue
);
781 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
782 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
|| Lun
> 0xFFFF) {
783 return EFI_NOT_FOUND
;
786 ScsiDevicePath
= AllocatePool (sizeof *ScsiDevicePath
);
787 if (ScsiDevicePath
== NULL
) {
788 return EFI_OUT_OF_RESOURCES
;
791 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
792 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
793 ScsiDevicePath
->Header
.Length
[0] = (UINT8
) sizeof *ScsiDevicePath
;
794 ScsiDevicePath
->Header
.Length
[1] = (UINT8
) (sizeof *ScsiDevicePath
>> 8);
795 ScsiDevicePath
->Pun
= TargetValue
;
796 ScsiDevicePath
->Lun
= (UINT16
) Lun
;
798 *DevicePath
= &ScsiDevicePath
->Header
;
805 VirtioScsiGetTargetLun (
806 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
807 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
808 OUT UINT8
**TargetPointer
,
812 SCSI_DEVICE_PATH
*ScsiDevicePath
;
816 if (DevicePath
== NULL
|| TargetPointer
== NULL
|| *TargetPointer
== NULL
||
818 return EFI_INVALID_PARAMETER
;
821 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
822 DevicePath
->SubType
!= MSG_SCSI_DP
) {
823 return EFI_UNSUPPORTED
;
826 ScsiDevicePath
= (SCSI_DEVICE_PATH
*) DevicePath
;
827 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
828 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
829 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
830 return EFI_NOT_FOUND
;
834 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
835 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
836 // c) ScsiDevicePath->Pun is an UINT16
838 Target
= *TargetPointer
;
839 CopyMem (Target
, &ScsiDevicePath
->Pun
, 2);
840 SetMem (Target
+ 2, TARGET_MAX_BYTES
- 2, 0x00);
842 *Lun
= ScsiDevicePath
->Lun
;
849 VirtioScsiResetChannel (
850 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
853 return EFI_UNSUPPORTED
;
859 VirtioScsiResetTargetLun (
860 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
865 return EFI_UNSUPPORTED
;
871 VirtioScsiGetNextTarget (
872 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
873 IN OUT UINT8
**TargetPointer
882 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
884 Target
= *TargetPointer
;
887 // Search for first non-0xFF byte. If not found, return first target.
889 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
&& Target
[Idx
] == 0xFF; ++Idx
)
891 if (Idx
== TARGET_MAX_BYTES
) {
892 SetMem (Target
, TARGET_MAX_BYTES
, 0x00);
897 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
899 CopyMem (&LastTarget
, Target
, sizeof LastTarget
);
902 // increment target if valid on input
904 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (This
);
905 if (LastTarget
> Dev
->MaxTarget
) {
906 return EFI_INVALID_PARAMETER
;
909 if (LastTarget
< Dev
->MaxTarget
) {
911 CopyMem (Target
, &LastTarget
, sizeof LastTarget
);
915 return EFI_NOT_FOUND
;
923 IN OUT VSCSI_DEV
*Dev
928 UINT64 RingBaseShift
;
930 UINT16 MaxChannel
; // for validation only
931 UINT32 NumQueues
; // for validation only
935 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
937 NextDevStat
= 0; // step 1 -- reset device
938 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
939 if (EFI_ERROR (Status
)) {
943 NextDevStat
|= VSTAT_ACK
; // step 2 -- acknowledge device presence
944 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
945 if (EFI_ERROR (Status
)) {
949 NextDevStat
|= VSTAT_DRIVER
; // step 3 -- we know how to drive it
950 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
951 if (EFI_ERROR (Status
)) {
956 // Set Page Size - MMIO VirtIo Specific
958 Status
= Dev
->VirtIo
->SetPageSize (Dev
->VirtIo
, EFI_PAGE_SIZE
);
959 if (EFI_ERROR (Status
)) {
964 // step 4a -- retrieve and validate features
966 Status
= Dev
->VirtIo
->GetDeviceFeatures (Dev
->VirtIo
, &Features
);
967 if (EFI_ERROR (Status
)) {
970 Dev
->InOutSupported
= (BOOLEAN
) ((Features
& VIRTIO_SCSI_F_INOUT
) != 0);
972 Status
= VIRTIO_CFG_READ (Dev
, MaxChannel
, &MaxChannel
);
973 if (EFI_ERROR (Status
)) {
976 if (MaxChannel
!= 0) {
978 // this driver is for a single-channel virtio-scsi HBA
980 Status
= EFI_UNSUPPORTED
;
984 Status
= VIRTIO_CFG_READ (Dev
, NumQueues
, &NumQueues
);
985 if (EFI_ERROR (Status
)) {
989 Status
= EFI_UNSUPPORTED
;
993 Status
= VIRTIO_CFG_READ (Dev
, MaxTarget
, &Dev
->MaxTarget
);
994 if (EFI_ERROR (Status
)) {
997 if (Dev
->MaxTarget
> PcdGet16 (PcdVirtioScsiMaxTargetLimit
)) {
998 Dev
->MaxTarget
= PcdGet16 (PcdVirtioScsiMaxTargetLimit
);
1001 Status
= VIRTIO_CFG_READ (Dev
, MaxLun
, &Dev
->MaxLun
);
1002 if (EFI_ERROR (Status
)) {
1005 if (Dev
->MaxLun
> PcdGet32 (PcdVirtioScsiMaxLunLimit
)) {
1006 Dev
->MaxLun
= PcdGet32 (PcdVirtioScsiMaxLunLimit
);
1009 Status
= VIRTIO_CFG_READ (Dev
, MaxSectors
, &Dev
->MaxSectors
);
1010 if (EFI_ERROR (Status
)) {
1013 if (Dev
->MaxSectors
< 2) {
1015 // We must be able to halve it for bidirectional transfers
1016 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
1018 Status
= EFI_UNSUPPORTED
;
1022 Features
&= VIRTIO_SCSI_F_INOUT
| VIRTIO_F_VERSION_1
|
1023 VIRTIO_F_IOMMU_PLATFORM
;
1026 // In virtio-1.0, feature negotiation is expected to complete before queue
1027 // discovery, and the device can also reject the selected set of features.
1029 if (Dev
->VirtIo
->Revision
>= VIRTIO_SPEC_REVISION (1, 0, 0)) {
1030 Status
= Virtio10WriteFeatures (Dev
->VirtIo
, Features
, &NextDevStat
);
1031 if (EFI_ERROR (Status
)) {
1037 // step 4b -- allocate request virtqueue
1039 Status
= Dev
->VirtIo
->SetQueueSel (Dev
->VirtIo
, VIRTIO_SCSI_REQUEST_QUEUE
);
1040 if (EFI_ERROR (Status
)) {
1043 Status
= Dev
->VirtIo
->GetQueueNumMax (Dev
->VirtIo
, &QueueSize
);
1044 if (EFI_ERROR (Status
)) {
1048 // VirtioScsiPassThru() uses at most four descriptors
1050 if (QueueSize
< 4) {
1051 Status
= EFI_UNSUPPORTED
;
1055 Status
= VirtioRingInit (Dev
->VirtIo
, QueueSize
, &Dev
->Ring
);
1056 if (EFI_ERROR (Status
)) {
1061 // If anything fails from here on, we must release the ring resources
1063 Status
= VirtioRingMap (
1069 if (EFI_ERROR (Status
)) {
1074 // Additional steps for MMIO: align the queue appropriately, and set the
1075 // size. If anything fails from here on, we must unmap the ring resources.
1077 Status
= Dev
->VirtIo
->SetQueueNum (Dev
->VirtIo
, QueueSize
);
1078 if (EFI_ERROR (Status
)) {
1082 Status
= Dev
->VirtIo
->SetQueueAlign (Dev
->VirtIo
, EFI_PAGE_SIZE
);
1083 if (EFI_ERROR (Status
)) {
1088 // step 4c -- Report GPFN (guest-physical frame number) of queue.
1090 Status
= Dev
->VirtIo
->SetQueueAddress (
1095 if (EFI_ERROR (Status
)) {
1100 // step 5 -- Report understood features and guest-tuneables.
1102 if (Dev
->VirtIo
->Revision
< VIRTIO_SPEC_REVISION (1, 0, 0)) {
1103 Features
&= ~(UINT64
)(VIRTIO_F_VERSION_1
| VIRTIO_F_IOMMU_PLATFORM
);
1104 Status
= Dev
->VirtIo
->SetGuestFeatures (Dev
->VirtIo
, Features
);
1105 if (EFI_ERROR (Status
)) {
1111 // We expect these maximum sizes from the host. Since they are
1112 // guest-negotiable, ask for them rather than just checking them.
1114 Status
= VIRTIO_CFG_WRITE (Dev
, CdbSize
, VIRTIO_SCSI_CDB_SIZE
);
1115 if (EFI_ERROR (Status
)) {
1118 Status
= VIRTIO_CFG_WRITE (Dev
, SenseSize
, VIRTIO_SCSI_SENSE_SIZE
);
1119 if (EFI_ERROR (Status
)) {
1124 // step 6 -- initialization complete
1126 NextDevStat
|= VSTAT_DRIVER_OK
;
1127 Status
= Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
1128 if (EFI_ERROR (Status
)) {
1133 // populate the exported interface's attributes
1135 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1136 Dev
->PassThru
.PassThru
= &VirtioScsiPassThru
;
1137 Dev
->PassThru
.GetNextTargetLun
= &VirtioScsiGetNextTargetLun
;
1138 Dev
->PassThru
.BuildDevicePath
= &VirtioScsiBuildDevicePath
;
1139 Dev
->PassThru
.GetTargetLun
= &VirtioScsiGetTargetLun
;
1140 Dev
->PassThru
.ResetChannel
= &VirtioScsiResetChannel
;
1141 Dev
->PassThru
.ResetTargetLun
= &VirtioScsiResetTargetLun
;
1142 Dev
->PassThru
.GetNextTarget
= &VirtioScsiGetNextTarget
;
1145 // AdapterId is a target for which no handle will be created during bus scan.
1146 // Prevent any conflict with real devices.
1148 Dev
->PassThruMode
.AdapterId
= 0xFFFFFFFF;
1151 // Set both physical and logical attributes for non-RAID SCSI channel. See
1152 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
1153 // SCSI Pass Thru Protocol.
1155 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1156 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1159 // no restriction on transfer buffer alignment
1161 Dev
->PassThruMode
.IoAlign
= 0;
1166 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RingMap
);
1169 VirtioRingUninit (Dev
->VirtIo
, &Dev
->Ring
);
1173 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
1174 // Status. VirtIo access failure here should not mask the original error.
1176 NextDevStat
|= VSTAT_FAILED
;
1177 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, NextDevStat
);
1179 Dev
->InOutSupported
= FALSE
;
1182 Dev
->MaxSectors
= 0;
1184 return Status
; // reached only via Failed above
1192 IN OUT VSCSI_DEV
*Dev
1196 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
1197 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
1198 // the old comms area.
1200 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
1202 Dev
->InOutSupported
= FALSE
;
1205 Dev
->MaxSectors
= 0;
1207 Dev
->VirtIo
->UnmapSharedBuffer (Dev
->VirtIo
, Dev
->RingMap
);
1208 VirtioRingUninit (Dev
->VirtIo
, &Dev
->Ring
);
1210 SetMem (&Dev
->PassThru
, sizeof Dev
->PassThru
, 0x00);
1211 SetMem (&Dev
->PassThruMode
, sizeof Dev
->PassThruMode
, 0x00);
1216 // Event notification function enqueued by ExitBootServices().
1222 VirtioScsiExitBoot (
1229 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1231 // Reset the device. This causes the hypervisor to forget about the virtio
1234 // We allocated said ring in EfiBootServicesData type memory, and code
1235 // executing after ExitBootServices() is permitted to overwrite it.
1238 Dev
->VirtIo
->SetDeviceStatus (Dev
->VirtIo
, 0);
1243 // Probe, start and stop functions of this driver, called by the DXE core for
1244 // specific devices.
1246 // The following specifications document these interfaces:
1247 // - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
1248 // - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
1250 // The implementation follows:
1251 // - Driver Writer's Guide for UEFI 2.3.1 v1.01
1252 // - 5.1.3.4 OpenProtocol() and CloseProtocol()
1253 // - UEFI Spec 2.3.1 + Errata C
1254 // - 6.3 Protocol Handler Services
1259 VirtioScsiDriverBindingSupported (
1260 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1261 IN EFI_HANDLE DeviceHandle
,
1262 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1266 VIRTIO_DEVICE_PROTOCOL
*VirtIo
;
1269 // Attempt to open the device with the VirtIo set of interfaces. On success,
1270 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
1271 // attempts (EFI_ALREADY_STARTED).
1273 Status
= gBS
->OpenProtocol (
1274 DeviceHandle
, // candidate device
1275 &gVirtioDeviceProtocolGuid
, // for generic VirtIo access
1276 (VOID
**)&VirtIo
, // handle to instantiate
1277 This
->DriverBindingHandle
, // requestor driver identity
1278 DeviceHandle
, // ControllerHandle, according to
1279 // the UEFI Driver Model
1280 EFI_OPEN_PROTOCOL_BY_DRIVER
// get exclusive VirtIo access to
1281 // the device; to be released
1283 if (EFI_ERROR (Status
)) {
1287 if (VirtIo
->SubSystemDeviceId
!= VIRTIO_SUBSYSTEM_SCSI_HOST
) {
1288 Status
= EFI_UNSUPPORTED
;
1292 // We needed VirtIo access only transitorily, to see whether we support the
1295 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1296 This
->DriverBindingHandle
, DeviceHandle
);
1303 VirtioScsiDriverBindingStart (
1304 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1305 IN EFI_HANDLE DeviceHandle
,
1306 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
1312 Dev
= (VSCSI_DEV
*) AllocateZeroPool (sizeof *Dev
);
1314 return EFI_OUT_OF_RESOURCES
;
1317 Status
= gBS
->OpenProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1318 (VOID
**)&Dev
->VirtIo
, This
->DriverBindingHandle
,
1319 DeviceHandle
, EFI_OPEN_PROTOCOL_BY_DRIVER
);
1320 if (EFI_ERROR (Status
)) {
1321 goto FreeVirtioScsi
;
1325 // VirtIo access granted, configure virtio-scsi device.
1327 Status
= VirtioScsiInit (Dev
);
1328 if (EFI_ERROR (Status
)) {
1332 Status
= gBS
->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES
, TPL_CALLBACK
,
1333 &VirtioScsiExitBoot
, Dev
, &Dev
->ExitBoot
);
1334 if (EFI_ERROR (Status
)) {
1339 // Setup complete, attempt to export the driver instance's PassThru
1342 Dev
->Signature
= VSCSI_SIG
;
1343 Status
= gBS
->InstallProtocolInterface (&DeviceHandle
,
1344 &gEfiExtScsiPassThruProtocolGuid
, EFI_NATIVE_INTERFACE
,
1346 if (EFI_ERROR (Status
)) {
1353 gBS
->CloseEvent (Dev
->ExitBoot
);
1356 VirtioScsiUninit (Dev
);
1359 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1360 This
->DriverBindingHandle
, DeviceHandle
);
1371 VirtioScsiDriverBindingStop (
1372 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1373 IN EFI_HANDLE DeviceHandle
,
1374 IN UINTN NumberOfChildren
,
1375 IN EFI_HANDLE
*ChildHandleBuffer
1379 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1382 Status
= gBS
->OpenProtocol (
1383 DeviceHandle
, // candidate device
1384 &gEfiExtScsiPassThruProtocolGuid
, // retrieve the SCSI iface
1385 (VOID
**)&PassThru
, // target pointer
1386 This
->DriverBindingHandle
, // requestor driver ident.
1387 DeviceHandle
, // lookup req. for dev.
1388 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// lookup only, no new ref.
1390 if (EFI_ERROR (Status
)) {
1394 Dev
= VIRTIO_SCSI_FROM_PASS_THRU (PassThru
);
1397 // Handle Stop() requests for in-use driver instances gracefully.
1399 Status
= gBS
->UninstallProtocolInterface (DeviceHandle
,
1400 &gEfiExtScsiPassThruProtocolGuid
, &Dev
->PassThru
);
1401 if (EFI_ERROR (Status
)) {
1405 gBS
->CloseEvent (Dev
->ExitBoot
);
1407 VirtioScsiUninit (Dev
);
1409 gBS
->CloseProtocol (DeviceHandle
, &gVirtioDeviceProtocolGuid
,
1410 This
->DriverBindingHandle
, DeviceHandle
);
1419 // The static object that groups the Supported() (ie. probe), Start() and
1420 // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1421 // C, 10.1 EFI Driver Binding Protocol.
1423 STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding
= {
1424 &VirtioScsiDriverBindingSupported
,
1425 &VirtioScsiDriverBindingStart
,
1426 &VirtioScsiDriverBindingStop
,
1427 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1428 NULL
, // ImageHandle, to be overwritten by
1429 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
1430 NULL
// DriverBindingHandle, ditto
1435 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1436 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1437 // in English, for display on standard console devices. This is recommended for
1438 // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1439 // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1441 // Device type names ("Virtio SCSI Host Device") are not formatted because the
1442 // driver supports only that device type. Therefore the driver name suffices
1443 // for unambiguous identification.
1447 EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1448 { "eng;en", L
"Virtio SCSI Host Driver" },
1453 EFI_COMPONENT_NAME_PROTOCOL gComponentName
;
1457 VirtioScsiGetDriverName (
1458 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1460 OUT CHAR16
**DriverName
1463 return LookupUnicodeString2 (
1465 This
->SupportedLanguages
,
1468 (BOOLEAN
)(This
== &gComponentName
) // Iso639Language
1474 VirtioScsiGetDeviceName (
1475 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1476 IN EFI_HANDLE DeviceHandle
,
1477 IN EFI_HANDLE ChildHandle
,
1479 OUT CHAR16
**ControllerName
1482 return EFI_UNSUPPORTED
;
1486 EFI_COMPONENT_NAME_PROTOCOL gComponentName
= {
1487 &VirtioScsiGetDriverName
,
1488 &VirtioScsiGetDeviceName
,
1489 "eng" // SupportedLanguages, ISO 639-2 language codes
1493 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2
= {
1494 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &VirtioScsiGetDriverName
,
1495 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &VirtioScsiGetDeviceName
,
1496 "en" // SupportedLanguages, RFC 4646 language codes
1501 // Entry point of this driver.
1505 VirtioScsiEntryPoint (
1506 IN EFI_HANDLE ImageHandle
,
1507 IN EFI_SYSTEM_TABLE
*SystemTable
1510 return EfiLibInstallDriverBindingComponentName2 (