3 This driver produces Extended SCSI Pass Thru Protocol instances for
6 Copyright (C) 2020, Oracle and/or its affiliates.
8 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <IndustryStandard/Pci.h>
13 #include <IndustryStandard/PvScsi.h>
14 #include <Library/BaseLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiLib.h>
19 #include <Protocol/PciIo.h>
20 #include <Protocol/PciRootBridgeIo.h>
21 #include <Uefi/UefiSpec.h>
26 // Higher versions will be used before lower, 0x10-0xffffffef is the version
27 // range for IHV (Indie Hardware Vendors)
29 #define PVSCSI_BINDING_VERSION 0x10
32 // Ext SCSI Pass Thru utilities
36 Reads a 32-bit value into BAR0 using MMIO
41 IN CONST PVSCSI_DEV
*Dev
,
46 return Dev
->PciIo
->Mem
.Read (
57 Writes a 32-bit value into BAR0 using MMIO
62 IN CONST PVSCSI_DEV
*Dev
,
67 return Dev
->PciIo
->Mem
.Write (
78 Writes multiple words of data into BAR0 using MMIO
82 PvScsiMmioWrite32Multiple (
83 IN CONST PVSCSI_DEV
*Dev
,
89 return Dev
->PciIo
->Mem
.Write (
91 EfiPciIoWidthFifoUint32
,
100 Send a PVSCSI command to device.
102 @param[in] Dev The pvscsi host device.
103 @param[in] Cmd The command to send to device.
104 @param[in] OPTIONAL DescWords An optional command descriptor (If command
105 have a descriptor). The descriptor is
106 provided as an array of UINT32 words and
107 is must be 32-bit aligned.
108 @param[in] DescWordsCount The number of words in command descriptor.
109 Caller must specify here 0 if DescWords
110 is not supplied (It is optional). In that
111 case, DescWords is ignored.
113 @return Status codes returned by Dev->PciIo->Mem.Write().
119 IN CONST PVSCSI_DEV
*Dev
,
121 IN UINT32
*DescWords OPTIONAL
,
122 IN UINTN DescWordsCount
127 if (DescWordsCount
> PVSCSI_MAX_CMD_DATA_WORDS
) {
128 return EFI_INVALID_PARAMETER
;
131 Status
= PvScsiMmioWrite32 (Dev
, PvScsiRegOffsetCommand
, Cmd
);
132 if (EFI_ERROR (Status
)) {
136 if (DescWordsCount
> 0) {
137 return PvScsiMmioWrite32Multiple (
139 PvScsiRegOffsetCommandData
,
151 IN CONST PVSCSI_DEV
*Dev
154 return PvScsiWriteCmdDesc (Dev
, PvScsiCmdAdapterReset
, NULL
, 0);
158 Returns if PVSCSI request ring is full
162 PvScsiIsReqRingFull (
163 IN CONST PVSCSI_DEV
*Dev
166 PVSCSI_RINGS_STATE
*RingsState
;
167 UINT32 ReqNumEntries
;
169 RingsState
= Dev
->RingDesc
.RingState
;
170 ReqNumEntries
= 1U << RingsState
->ReqNumEntriesLog2
;
171 return (RingsState
->ReqProdIdx
- RingsState
->CmpConsIdx
) >= ReqNumEntries
;
175 Returns pointer to current request descriptor to produce
178 PVSCSI_RING_REQ_DESC
*
179 PvScsiGetCurrentRequest (
180 IN CONST PVSCSI_DEV
*Dev
183 PVSCSI_RINGS_STATE
*RingState
;
184 UINT32 ReqNumEntries
;
186 RingState
= Dev
->RingDesc
.RingState
;
187 ReqNumEntries
= 1U << RingState
->ReqNumEntriesLog2
;
188 return Dev
->RingDesc
.RingReqs
+
189 (RingState
->ReqProdIdx
& (ReqNumEntries
- 1));
193 Returns pointer to current completion descriptor to consume
196 PVSCSI_RING_CMP_DESC
*
197 PvScsiGetCurrentResponse (
198 IN CONST PVSCSI_DEV
*Dev
201 PVSCSI_RINGS_STATE
*RingState
;
202 UINT32 CmpNumEntries
;
204 RingState
= Dev
->RingDesc
.RingState
;
205 CmpNumEntries
= 1U << RingState
->CmpNumEntriesLog2
;
206 return Dev
->RingDesc
.RingCmps
+
207 (RingState
->CmpConsIdx
& (CmpNumEntries
- 1));
211 Wait for device to signal completion of submitted requests
215 PvScsiWaitForRequestCompletion (
216 IN CONST PVSCSI_DEV
*Dev
223 // Note: We don't yet support Timeout according to
224 // EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.Timeout.
226 // This is consistent with some other Scsi PassThru drivers
227 // such as VirtioScsi.
230 Status
= PvScsiMmioRead32 (Dev
, PvScsiRegOffsetIntrStatus
, &IntrStatus
);
231 if (EFI_ERROR (Status
)) {
236 // PVSCSI_INTR_CMPL_MASK is set if device completed submitted requests
238 if ((IntrStatus
& PVSCSI_INTR_CMPL_MASK
) != 0) {
242 gBS
->Stall (Dev
->WaitForCmpStallInUsecs
);
246 // Acknowledge PVSCSI_INTR_CMPL_MASK in device interrupt-status register
248 return PvScsiMmioWrite32 (
250 PvScsiRegOffsetIntrStatus
,
251 PVSCSI_INTR_CMPL_MASK
256 Create a fake host adapter error
260 ReportHostAdapterError (
261 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
264 Packet
->InTransferLength
= 0;
265 Packet
->OutTransferLength
= 0;
266 Packet
->SenseDataLength
= 0;
267 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
268 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
269 return EFI_DEVICE_ERROR
;
273 Create a fake host adapter overrun error
277 ReportHostAdapterOverrunError (
278 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
281 Packet
->SenseDataLength
= 0;
282 Packet
->HostAdapterStatus
=
283 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
284 Packet
->TargetStatus
= EFI_EXT_SCSI_STATUS_TARGET_GOOD
;
285 return EFI_BAD_BUFFER_SIZE
;
289 Populate a PVSCSI request descriptor from the Extended SCSI Pass Thru
295 IN CONST PVSCSI_DEV
*Dev
,
298 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
299 OUT PVSCSI_RING_REQ_DESC
*Request
305 // We only use first byte of target identifer
307 TargetValue
= *Target
;
310 // Check for unsupported requests
314 // Bidirectional transfer was requested
316 (Packet
->InTransferLength
> 0 && Packet
->OutTransferLength
> 0) ||
317 (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
) ||
319 // Command Descriptor Block bigger than this constant should be considered
320 // out-of-band. We currently don't support these CDBs.
322 (Packet
->CdbLength
> PVSCSI_CDB_MAX_SIZE
)
326 // This error code doesn't require updates to the Packet output fields
328 return EFI_UNSUPPORTED
;
332 // Check for invalid parameters
336 // Addressed invalid device
338 (TargetValue
> Dev
->MaxTarget
) || (Lun
> Dev
->MaxLun
) ||
340 // Invalid direction (there doesn't seem to be a macro for the "no data
341 // transferred" "direction", eg. for TEST UNIT READY)
343 (Packet
->DataDirection
> EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL
) ||
345 // Trying to receive, but destination pointer is NULL, or contradicting
346 // transfer direction
348 ((Packet
->InTransferLength
> 0) &&
349 ((Packet
->InDataBuffer
== NULL
) ||
350 (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_WRITE
)
354 // Trying to send, but source pointer is NULL, or contradicting
355 // transfer direction
357 ((Packet
->OutTransferLength
> 0) &&
358 ((Packet
->OutDataBuffer
== NULL
) ||
359 (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
)
365 // This error code doesn't require updates to the Packet output fields
367 return EFI_INVALID_PARAMETER
;
371 // Check for input/output buffer too large for DMA communication buffer
373 if (Packet
->InTransferLength
> sizeof (Dev
->DmaBuf
->Data
)) {
374 Packet
->InTransferLength
= sizeof (Dev
->DmaBuf
->Data
);
375 return ReportHostAdapterOverrunError (Packet
);
377 if (Packet
->OutTransferLength
> sizeof (Dev
->DmaBuf
->Data
)) {
378 Packet
->OutTransferLength
= sizeof (Dev
->DmaBuf
->Data
);
379 return ReportHostAdapterOverrunError (Packet
);
383 // Encode PVSCSI request
385 ZeroMem (Request
, sizeof (*Request
));
388 Request
->Target
= TargetValue
;
390 // This cast is safe as PVSCSI_DEV.MaxLun is defined as UINT8
392 Request
->Lun
[1] = (UINT8
)Lun
;
393 Request
->SenseLen
= Packet
->SenseDataLength
;
395 // DMA communication buffer SenseData overflow is not possible
396 // due to Packet->SenseDataLength defined as UINT8
398 Request
->SenseAddr
= PVSCSI_DMA_BUF_DEV_ADDR (Dev
, SenseData
);
399 Request
->CdbLen
= Packet
->CdbLength
;
400 CopyMem (Request
->Cdb
, Packet
->Cdb
, Packet
->CdbLength
);
401 Request
->VcpuHint
= 0;
402 Request
->Tag
= PVSCSI_SIMPLE_QUEUE_TAG
;
403 if (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
) {
404 Request
->Flags
= PVSCSI_FLAG_CMD_DIR_TOHOST
;
405 Request
->DataLen
= Packet
->InTransferLength
;
407 Request
->Flags
= PVSCSI_FLAG_CMD_DIR_TODEVICE
;
408 Request
->DataLen
= Packet
->OutTransferLength
;
411 Packet
->OutDataBuffer
,
412 Packet
->OutTransferLength
415 Request
->DataAddr
= PVSCSI_DMA_BUF_DEV_ADDR (Dev
, Data
);
421 Handle the PVSCSI device response:
422 - Copy returned data from DMA communication buffer.
423 - Update fields in Extended SCSI Pass Thru Protocol packet as required.
424 - Translate response code to EFI status code and host adapter status.
430 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
431 IN CONST PVSCSI_RING_CMP_DESC
*Response
435 // Fix SenseDataLength to amount of data returned
437 if (Packet
->SenseDataLength
> Response
->SenseLen
) {
438 Packet
->SenseDataLength
= (UINT8
)Response
->SenseLen
;
441 // Copy sense data from DMA communication buffer
445 Dev
->DmaBuf
->SenseData
,
446 Packet
->SenseDataLength
450 // Copy device output from DMA communication buffer
452 if (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
) {
453 CopyMem (Packet
->InDataBuffer
, Dev
->DmaBuf
->Data
, Packet
->InTransferLength
);
457 // Report target status
459 Packet
->TargetStatus
= Response
->ScsiStatus
;
462 // Host adapter status and function return value depend on
463 // device response's host status
465 switch (Response
->HostStatus
) {
466 case PvScsiBtStatSuccess
:
467 case PvScsiBtStatLinkedCommandCompleted
:
468 case PvScsiBtStatLinkedCommandCompletedWithFlag
:
469 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
472 case PvScsiBtStatDataUnderrun
:
474 // Report transferred amount in underrun
476 if (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
) {
477 Packet
->InTransferLength
= (UINT32
)Response
->DataLen
;
479 Packet
->OutTransferLength
= (UINT32
)Response
->DataLen
;
481 Packet
->HostAdapterStatus
=
482 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
485 case PvScsiBtStatDatarun
:
486 Packet
->HostAdapterStatus
=
487 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
490 case PvScsiBtStatSelTimeout
:
491 Packet
->HostAdapterStatus
=
492 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT
;
495 case PvScsiBtStatBusFree
:
496 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE
;
499 case PvScsiBtStatInvPhase
:
500 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR
;
503 case PvScsiBtStatSensFailed
:
504 Packet
->HostAdapterStatus
=
505 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED
;
508 case PvScsiBtStatTagReject
:
509 case PvScsiBtStatBadMsg
:
510 Packet
->HostAdapterStatus
=
511 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT
;
514 case PvScsiBtStatBusReset
:
515 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
518 case PvScsiBtStatHaTimeout
:
519 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT
;
522 case PvScsiBtStatScsiParity
:
523 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR
;
527 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
531 return EFI_DEVICE_ERROR
;
535 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
536 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
540 IsTargetInitialized (
546 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
547 if (Target
[Idx
] != 0xFF) {
555 // Ext SCSI Pass Thru
562 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
565 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
566 IN EFI_EVENT Event OPTIONAL
571 PVSCSI_RING_REQ_DESC
*Request
;
572 PVSCSI_RING_CMP_DESC
*Response
;
574 Dev
= PVSCSI_FROM_PASS_THRU (This
);
576 if (PvScsiIsReqRingFull (Dev
)) {
577 return EFI_NOT_READY
;
580 Request
= PvScsiGetCurrentRequest (Dev
);
582 Status
= PopulateRequest (Dev
, Target
, Lun
, Packet
, Request
);
583 if (EFI_ERROR (Status
)) {
588 // Writes to Request must be globally visible before making request
589 // available to device
592 Dev
->RingDesc
.RingState
->ReqProdIdx
++;
594 Status
= PvScsiMmioWrite32 (Dev
, PvScsiRegOffsetKickRwIo
, 0);
595 if (EFI_ERROR (Status
)) {
597 // If kicking the host fails, we must fake a host adapter error.
598 // EFI_NOT_READY would save us the effort, but it would also suggest that
601 return ReportHostAdapterError (Packet
);
604 Status
= PvScsiWaitForRequestCompletion (Dev
);
605 if (EFI_ERROR (Status
)) {
607 // If waiting for request completion fails, we must fake a host adapter
608 // error. EFI_NOT_READY would save us the effort, but it would also suggest
609 // that the caller retry.
611 return ReportHostAdapterError (Packet
);
614 Response
= PvScsiGetCurrentResponse (Dev
);
615 Status
= HandleResponse (Dev
, Packet
, Response
);
618 // Reads from response must complete before releasing completion entry
622 Dev
->RingDesc
.RingState
->CmpConsIdx
++;
630 PvScsiGetNextTargetLun (
631 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
632 IN OUT UINT8
**Target
,
640 if (Target
== NULL
) {
641 return EFI_INVALID_PARAMETER
;
645 // The Target input parameter is unnecessarily a pointer-to-pointer
650 // If target not initialized, return first target & LUN
652 if (!IsTargetInitialized (TargetPtr
)) {
653 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
659 // We only use first byte of target identifer
661 LastTarget
= *TargetPtr
;
664 // Increment (target, LUN) pair if valid on input
666 Dev
= PVSCSI_FROM_PASS_THRU (This
);
667 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
668 return EFI_INVALID_PARAMETER
;
671 if (*Lun
< Dev
->MaxLun
) {
676 if (LastTarget
< Dev
->MaxTarget
) {
679 *TargetPtr
= LastTarget
;
683 return EFI_NOT_FOUND
;
689 PvScsiBuildDevicePath (
690 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
693 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
698 SCSI_DEVICE_PATH
*ScsiDevicePath
;
700 if (DevicePath
== NULL
) {
701 return EFI_INVALID_PARAMETER
;
705 // We only use first byte of target identifer
707 TargetValue
= *Target
;
709 Dev
= PVSCSI_FROM_PASS_THRU (This
);
710 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
) {
711 return EFI_NOT_FOUND
;
714 ScsiDevicePath
= AllocatePool (sizeof (*ScsiDevicePath
));
715 if (ScsiDevicePath
== NULL
) {
716 return EFI_OUT_OF_RESOURCES
;
719 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
720 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
721 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
722 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
723 ScsiDevicePath
->Pun
= TargetValue
;
724 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
726 *DevicePath
= &ScsiDevicePath
->Header
;
734 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
735 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
740 SCSI_DEVICE_PATH
*ScsiDevicePath
;
743 if (DevicePath
== NULL
|| Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
744 return EFI_INVALID_PARAMETER
;
747 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
748 DevicePath
->SubType
!= MSG_SCSI_DP
) {
749 return EFI_UNSUPPORTED
;
752 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
753 Dev
= PVSCSI_FROM_PASS_THRU (This
);
754 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
755 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
756 return EFI_NOT_FOUND
;
760 // We only use first byte of target identifer
762 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
763 ZeroMem (*Target
+ 1, TARGET_MAX_BYTES
- 1);
764 *Lun
= ScsiDevicePath
->Lun
;
773 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
776 return EFI_UNSUPPORTED
;
782 PvScsiResetTargetLun (
783 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
788 return EFI_UNSUPPORTED
;
794 PvScsiGetNextTarget (
795 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
796 IN OUT UINT8
**Target
803 if (Target
== NULL
) {
804 return EFI_INVALID_PARAMETER
;
808 // The Target input parameter is unnecessarily a pointer-to-pointer
813 // If target not initialized, return first target
815 if (!IsTargetInitialized (TargetPtr
)) {
816 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
821 // We only use first byte of target identifer
823 LastTarget
= *TargetPtr
;
826 // Increment target if valid on input
828 Dev
= PVSCSI_FROM_PASS_THRU (This
);
829 if (LastTarget
> Dev
->MaxTarget
) {
830 return EFI_INVALID_PARAMETER
;
833 if (LastTarget
< Dev
->MaxTarget
) {
835 *TargetPtr
= LastTarget
;
839 return EFI_NOT_FOUND
;
844 PvScsiSetPciAttributes (
845 IN OUT PVSCSI_DEV
*Dev
851 // Backup original PCI Attributes
853 Status
= Dev
->PciIo
->Attributes (
855 EfiPciIoAttributeOperationGet
,
857 &Dev
->OriginalPciAttributes
859 if (EFI_ERROR (Status
)) {
864 // Enable MMIO-Space & Bus-Mastering
866 Status
= Dev
->PciIo
->Attributes (
868 EfiPciIoAttributeOperationEnable
,
869 (EFI_PCI_IO_ATTRIBUTE_MEMORY
|
870 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER
),
873 if (EFI_ERROR (Status
)) {
882 PvScsiRestorePciAttributes (
886 Dev
->PciIo
->Attributes (
888 EfiPciIoAttributeOperationSet
,
889 Dev
->OriginalPciAttributes
,
896 PvScsiAllocateSharedPages (
899 OUT VOID
**HostAddress
,
900 OUT PVSCSI_DMA_DESC
*DmaDesc
906 Status
= Dev
->PciIo
->AllocateBuffer (
912 EFI_PCI_ATTRIBUTE_MEMORY_CACHED
914 if (EFI_ERROR (Status
)) {
918 NumberOfBytes
= EFI_PAGES_TO_SIZE (Pages
);
919 Status
= Dev
->PciIo
->Map (
921 EfiPciIoOperationBusMasterCommonBuffer
,
924 &DmaDesc
->DeviceAddress
,
927 if (EFI_ERROR (Status
)) {
931 if (NumberOfBytes
!= EFI_PAGES_TO_SIZE (Pages
)) {
932 Status
= EFI_OUT_OF_RESOURCES
;
939 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
942 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, *HostAddress
);
949 PvScsiFreeSharedPages (
952 IN VOID
*HostAddress
,
953 IN PVSCSI_DMA_DESC
*DmaDesc
956 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
957 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, HostAddress
);
963 IN OUT PVSCSI_DEV
*Dev
968 PVSCSI_CMD_DESC_SETUP_RINGS Cmd
;
971 PVSCSI_CMD_DESC_SETUP_RINGS
*Cmd
;
973 Cmd
= &AlignedCmd
.Cmd
;
975 Status
= PvScsiAllocateSharedPages (
978 (VOID
**)&Dev
->RingDesc
.RingState
,
979 &Dev
->RingDesc
.RingStateDmaDesc
981 if (EFI_ERROR (Status
)) {
984 ZeroMem (Dev
->RingDesc
.RingState
, EFI_PAGE_SIZE
);
986 Status
= PvScsiAllocateSharedPages (
989 (VOID
**)&Dev
->RingDesc
.RingReqs
,
990 &Dev
->RingDesc
.RingReqsDmaDesc
992 if (EFI_ERROR (Status
)) {
995 ZeroMem (Dev
->RingDesc
.RingReqs
, EFI_PAGE_SIZE
);
997 Status
= PvScsiAllocateSharedPages (
1000 (VOID
**)&Dev
->RingDesc
.RingCmps
,
1001 &Dev
->RingDesc
.RingCmpsDmaDesc
1003 if (EFI_ERROR (Status
)) {
1006 ZeroMem (Dev
->RingDesc
.RingCmps
, EFI_PAGE_SIZE
);
1008 ZeroMem (Cmd
, sizeof (*Cmd
));
1009 Cmd
->ReqRingNumPages
= 1;
1010 Cmd
->CmpRingNumPages
= 1;
1011 Cmd
->RingsStatePPN
= RShiftU64 (
1012 Dev
->RingDesc
.RingStateDmaDesc
.DeviceAddress
,
1015 Cmd
->ReqRingPPNs
[0] = RShiftU64 (
1016 Dev
->RingDesc
.RingReqsDmaDesc
.DeviceAddress
,
1019 Cmd
->CmpRingPPNs
[0] = RShiftU64 (
1020 Dev
->RingDesc
.RingCmpsDmaDesc
.DeviceAddress
,
1025 sizeof (*Cmd
) % sizeof (UINT32
) == 0,
1026 "Cmd must be multiple of 32-bit words"
1028 Status
= PvScsiWriteCmdDesc (
1030 PvScsiCmdSetupRings
,
1032 sizeof (*Cmd
) / sizeof (UINT32
)
1034 if (EFI_ERROR (Status
)) {
1041 PvScsiFreeSharedPages (
1044 Dev
->RingDesc
.RingCmps
,
1045 &Dev
->RingDesc
.RingCmpsDmaDesc
1049 PvScsiFreeSharedPages (
1052 Dev
->RingDesc
.RingReqs
,
1053 &Dev
->RingDesc
.RingReqsDmaDesc
1057 PvScsiFreeSharedPages (
1060 Dev
->RingDesc
.RingState
,
1061 &Dev
->RingDesc
.RingStateDmaDesc
1070 IN OUT PVSCSI_DEV
*Dev
1073 PvScsiFreeSharedPages (
1076 Dev
->RingDesc
.RingCmps
,
1077 &Dev
->RingDesc
.RingCmpsDmaDesc
1080 PvScsiFreeSharedPages (
1083 Dev
->RingDesc
.RingReqs
,
1084 &Dev
->RingDesc
.RingReqsDmaDesc
1087 PvScsiFreeSharedPages (
1090 Dev
->RingDesc
.RingState
,
1091 &Dev
->RingDesc
.RingStateDmaDesc
1098 IN OUT PVSCSI_DEV
*Dev
1104 // Init configuration
1106 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
1107 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
1108 Dev
->WaitForCmpStallInUsecs
= PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs
);
1111 // Set PCI Attributes
1113 Status
= PvScsiSetPciAttributes (Dev
);
1114 if (EFI_ERROR (Status
)) {
1121 Status
= PvScsiResetAdapter (Dev
);
1122 if (EFI_ERROR (Status
)) {
1123 goto RestorePciAttributes
;
1127 // Init PVSCSI rings
1129 Status
= PvScsiInitRings (Dev
);
1130 if (EFI_ERROR (Status
)) {
1131 goto RestorePciAttributes
;
1135 // Allocate DMA communication buffer
1137 Status
= PvScsiAllocateSharedPages (
1139 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1140 (VOID
**)&Dev
->DmaBuf
,
1143 if (EFI_ERROR (Status
)) {
1148 // Populate the exported interface's attributes
1150 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1151 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
1152 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
1153 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
1154 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
1155 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
1156 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
1157 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
1160 // AdapterId is a target for which no handle will be created during bus scan.
1161 // Prevent any conflict with real devices.
1163 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
1166 // Set both physical and logical attributes for non-RAID SCSI channel
1168 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1169 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1172 // No restriction on transfer buffer alignment
1174 Dev
->PassThruMode
.IoAlign
= 0;
1180 // Reset device to stop device usage of the rings.
1181 // This is required to safely free the rings.
1183 PvScsiResetAdapter (Dev
);
1185 PvScsiFreeRings (Dev
);
1187 RestorePciAttributes
:
1188 PvScsiRestorePciAttributes (Dev
);
1196 IN OUT PVSCSI_DEV
*Dev
1201 // - Make device stop processing all requests.
1202 // - Stop device usage of the rings.
1204 // This is required to safely free the DMA communication buffer
1207 PvScsiResetAdapter (Dev
);
1210 // Free DMA communication buffer
1212 PvScsiFreeSharedPages (
1214 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1219 PvScsiFreeRings (Dev
);
1221 PvScsiRestorePciAttributes (Dev
);
1225 Event notification called by ExitBootServices()
1238 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1241 // Reset the device to stop device usage of the rings.
1243 // We allocated said rings in EfiBootServicesData type memory, and code
1244 // executing after ExitBootServices() is permitted to overwrite it.
1246 PvScsiResetAdapter (Dev
);
1256 PvScsiDriverBindingSupported (
1257 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1258 IN EFI_HANDLE ControllerHandle
,
1259 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1263 EFI_PCI_IO_PROTOCOL
*PciIo
;
1266 Status
= gBS
->OpenProtocol (
1268 &gEfiPciIoProtocolGuid
,
1270 This
->DriverBindingHandle
,
1272 EFI_OPEN_PROTOCOL_BY_DRIVER
1274 if (EFI_ERROR (Status
)) {
1278 Status
= PciIo
->Pci
.Read (
1280 EfiPciIoWidthUint32
,
1282 sizeof (Pci
) / sizeof (UINT32
),
1285 if (EFI_ERROR (Status
)) {
1289 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
1290 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
1291 Status
= EFI_UNSUPPORTED
;
1295 Status
= EFI_SUCCESS
;
1298 gBS
->CloseProtocol (
1300 &gEfiPciIoProtocolGuid
,
1301 This
->DriverBindingHandle
,
1311 PvScsiDriverBindingStart (
1312 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1313 IN EFI_HANDLE ControllerHandle
,
1314 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1320 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
1322 return EFI_OUT_OF_RESOURCES
;
1325 Status
= gBS
->OpenProtocol (
1327 &gEfiPciIoProtocolGuid
,
1328 (VOID
**)&Dev
->PciIo
,
1329 This
->DriverBindingHandle
,
1331 EFI_OPEN_PROTOCOL_BY_DRIVER
1333 if (EFI_ERROR (Status
)) {
1337 Status
= PvScsiInit (Dev
);
1338 if (EFI_ERROR (Status
)) {
1342 Status
= gBS
->CreateEvent (
1343 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
1349 if (EFI_ERROR (Status
)) {
1354 // Setup complete, attempt to export the driver instance's PassThru interface
1356 Dev
->Signature
= PVSCSI_SIG
;
1357 Status
= gBS
->InstallProtocolInterface (
1359 &gEfiExtScsiPassThruProtocolGuid
,
1360 EFI_NATIVE_INTERFACE
,
1363 if (EFI_ERROR (Status
)) {
1370 gBS
->CloseEvent (Dev
->ExitBoot
);
1376 gBS
->CloseProtocol (
1378 &gEfiPciIoProtocolGuid
,
1379 This
->DriverBindingHandle
,
1392 PvScsiDriverBindingStop (
1393 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1394 IN EFI_HANDLE ControllerHandle
,
1395 IN UINTN NumberOfChildren
,
1396 IN EFI_HANDLE
*ChildHandleBuffer
1400 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1403 Status
= gBS
->OpenProtocol (
1405 &gEfiExtScsiPassThruProtocolGuid
,
1407 This
->DriverBindingHandle
,
1409 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
1411 if (EFI_ERROR (Status
)) {
1415 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
1417 Status
= gBS
->UninstallProtocolInterface (
1419 &gEfiExtScsiPassThruProtocolGuid
,
1422 if (EFI_ERROR (Status
)) {
1426 gBS
->CloseEvent (Dev
->ExitBoot
);
1430 gBS
->CloseProtocol (
1432 &gEfiPciIoProtocolGuid
,
1433 This
->DriverBindingHandle
,
1442 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
1443 &PvScsiDriverBindingSupported
,
1444 &PvScsiDriverBindingStart
,
1445 &PvScsiDriverBindingStop
,
1446 PVSCSI_BINDING_VERSION
,
1447 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
1448 NULL
// DriverBindingHandle, filled as well
1455 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1456 { "eng;en", L
"PVSCSI Host Driver" },
1460 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
1465 PvScsiGetDriverName (
1466 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1468 OUT CHAR16
**DriverName
1471 return LookupUnicodeString2 (
1473 This
->SupportedLanguages
,
1476 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
1483 PvScsiGetDeviceName (
1484 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1485 IN EFI_HANDLE DeviceHandle
,
1486 IN EFI_HANDLE ChildHandle
,
1488 OUT CHAR16
**ControllerName
1491 return EFI_UNSUPPORTED
;
1494 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
1495 &PvScsiGetDriverName
,
1496 &PvScsiGetDeviceName
,
1497 "eng" // SupportedLanguages, ISO 639-2 language codes
1500 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
1501 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
1502 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
1503 "en" // SupportedLanguages, RFC 4646 language codes
1513 IN EFI_HANDLE ImageHandle
,
1514 IN EFI_SYSTEM_TABLE
*SystemTable
1517 return EfiLibInstallDriverBindingComponentName2 (
1520 &mPvScsiDriverBinding
,