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
458 // (Strangely, PVSCSI interface defines Response->ScsiStatus as UINT16.
459 // But it should de-facto always have a value that fits UINT8. To avoid
460 // unexpected behavior, verify value is in UINT8 bounds before casting)
462 ASSERT (Response
->ScsiStatus
<= MAX_UINT8
);
463 Packet
->TargetStatus
= (UINT8
)Response
->ScsiStatus
;
466 // Host adapter status and function return value depend on
467 // device response's host status
469 switch (Response
->HostStatus
) {
470 case PvScsiBtStatSuccess
:
471 case PvScsiBtStatLinkedCommandCompleted
:
472 case PvScsiBtStatLinkedCommandCompletedWithFlag
:
473 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
;
476 case PvScsiBtStatDataUnderrun
:
478 // Report transferred amount in underrun
480 if (Packet
->DataDirection
== EFI_EXT_SCSI_DATA_DIRECTION_READ
) {
481 Packet
->InTransferLength
= (UINT32
)Response
->DataLen
;
483 Packet
->OutTransferLength
= (UINT32
)Response
->DataLen
;
485 Packet
->HostAdapterStatus
=
486 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
489 case PvScsiBtStatDatarun
:
490 Packet
->HostAdapterStatus
=
491 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN
;
494 case PvScsiBtStatSelTimeout
:
495 Packet
->HostAdapterStatus
=
496 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT
;
499 case PvScsiBtStatBusFree
:
500 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE
;
503 case PvScsiBtStatInvPhase
:
504 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR
;
507 case PvScsiBtStatSensFailed
:
508 Packet
->HostAdapterStatus
=
509 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED
;
512 case PvScsiBtStatTagReject
:
513 case PvScsiBtStatBadMsg
:
514 Packet
->HostAdapterStatus
=
515 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT
;
518 case PvScsiBtStatBusReset
:
519 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET
;
522 case PvScsiBtStatHaTimeout
:
523 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT
;
526 case PvScsiBtStatScsiParity
:
527 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR
;
531 Packet
->HostAdapterStatus
= EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER
;
535 return EFI_DEVICE_ERROR
;
539 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
540 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
544 IsTargetInitialized (
550 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
551 if (Target
[Idx
] != 0xFF) {
559 // Ext SCSI Pass Thru
566 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
569 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
570 IN EFI_EVENT Event OPTIONAL
575 PVSCSI_RING_REQ_DESC
*Request
;
576 PVSCSI_RING_CMP_DESC
*Response
;
578 Dev
= PVSCSI_FROM_PASS_THRU (This
);
580 if (PvScsiIsReqRingFull (Dev
)) {
581 return EFI_NOT_READY
;
584 Request
= PvScsiGetCurrentRequest (Dev
);
586 Status
= PopulateRequest (Dev
, Target
, Lun
, Packet
, Request
);
587 if (EFI_ERROR (Status
)) {
592 // Writes to Request must be globally visible before making request
593 // available to device
596 Dev
->RingDesc
.RingState
->ReqProdIdx
++;
598 Status
= PvScsiMmioWrite32 (Dev
, PvScsiRegOffsetKickRwIo
, 0);
599 if (EFI_ERROR (Status
)) {
601 // If kicking the host fails, we must fake a host adapter error.
602 // EFI_NOT_READY would save us the effort, but it would also suggest that
605 return ReportHostAdapterError (Packet
);
608 Status
= PvScsiWaitForRequestCompletion (Dev
);
609 if (EFI_ERROR (Status
)) {
611 // If waiting for request completion fails, we must fake a host adapter
612 // error. EFI_NOT_READY would save us the effort, but it would also suggest
613 // that the caller retry.
615 return ReportHostAdapterError (Packet
);
618 Response
= PvScsiGetCurrentResponse (Dev
);
619 Status
= HandleResponse (Dev
, Packet
, Response
);
622 // Reads from response must complete before releasing completion entry
626 Dev
->RingDesc
.RingState
->CmpConsIdx
++;
634 PvScsiGetNextTargetLun (
635 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
636 IN OUT UINT8
**Target
,
644 if (Target
== NULL
) {
645 return EFI_INVALID_PARAMETER
;
649 // The Target input parameter is unnecessarily a pointer-to-pointer
654 // If target not initialized, return first target & LUN
656 if (!IsTargetInitialized (TargetPtr
)) {
657 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
663 // We only use first byte of target identifer
665 LastTarget
= *TargetPtr
;
668 // Increment (target, LUN) pair if valid on input
670 Dev
= PVSCSI_FROM_PASS_THRU (This
);
671 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
672 return EFI_INVALID_PARAMETER
;
675 if (*Lun
< Dev
->MaxLun
) {
680 if (LastTarget
< Dev
->MaxTarget
) {
683 *TargetPtr
= LastTarget
;
687 return EFI_NOT_FOUND
;
693 PvScsiBuildDevicePath (
694 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
697 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
702 SCSI_DEVICE_PATH
*ScsiDevicePath
;
704 if (DevicePath
== NULL
) {
705 return EFI_INVALID_PARAMETER
;
709 // We only use first byte of target identifer
711 TargetValue
= *Target
;
713 Dev
= PVSCSI_FROM_PASS_THRU (This
);
714 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
) {
715 return EFI_NOT_FOUND
;
718 ScsiDevicePath
= AllocatePool (sizeof (*ScsiDevicePath
));
719 if (ScsiDevicePath
== NULL
) {
720 return EFI_OUT_OF_RESOURCES
;
723 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
724 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
725 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
726 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
727 ScsiDevicePath
->Pun
= TargetValue
;
728 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
730 *DevicePath
= &ScsiDevicePath
->Header
;
738 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
739 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
744 SCSI_DEVICE_PATH
*ScsiDevicePath
;
747 if (DevicePath
== NULL
|| Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
748 return EFI_INVALID_PARAMETER
;
751 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
752 DevicePath
->SubType
!= MSG_SCSI_DP
) {
753 return EFI_UNSUPPORTED
;
756 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
757 Dev
= PVSCSI_FROM_PASS_THRU (This
);
758 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
759 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
760 return EFI_NOT_FOUND
;
764 // We only use first byte of target identifer
766 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
767 ZeroMem (*Target
+ 1, TARGET_MAX_BYTES
- 1);
768 *Lun
= ScsiDevicePath
->Lun
;
777 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
780 return EFI_UNSUPPORTED
;
786 PvScsiResetTargetLun (
787 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
792 return EFI_UNSUPPORTED
;
798 PvScsiGetNextTarget (
799 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
800 IN OUT UINT8
**Target
807 if (Target
== NULL
) {
808 return EFI_INVALID_PARAMETER
;
812 // The Target input parameter is unnecessarily a pointer-to-pointer
817 // If target not initialized, return first target
819 if (!IsTargetInitialized (TargetPtr
)) {
820 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
825 // We only use first byte of target identifer
827 LastTarget
= *TargetPtr
;
830 // Increment target if valid on input
832 Dev
= PVSCSI_FROM_PASS_THRU (This
);
833 if (LastTarget
> Dev
->MaxTarget
) {
834 return EFI_INVALID_PARAMETER
;
837 if (LastTarget
< Dev
->MaxTarget
) {
839 *TargetPtr
= LastTarget
;
843 return EFI_NOT_FOUND
;
848 PvScsiSetPciAttributes (
849 IN OUT PVSCSI_DEV
*Dev
855 // Backup original PCI Attributes
857 Status
= Dev
->PciIo
->Attributes (
859 EfiPciIoAttributeOperationGet
,
861 &Dev
->OriginalPciAttributes
863 if (EFI_ERROR (Status
)) {
868 // Enable MMIO-Space & Bus-Mastering
870 Status
= Dev
->PciIo
->Attributes (
872 EfiPciIoAttributeOperationEnable
,
873 (EFI_PCI_IO_ATTRIBUTE_MEMORY
|
874 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER
),
877 if (EFI_ERROR (Status
)) {
882 // Signal device supports 64-bit DMA addresses
884 Status
= Dev
->PciIo
->Attributes (
886 EfiPciIoAttributeOperationEnable
,
887 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE
,
890 if (EFI_ERROR (Status
)) {
892 // Warn user that device will only be using 32-bit DMA addresses.
894 // Note that this does not prevent the device/driver from working
895 // and therefore we only warn and continue as usual.
899 "%a: failed to enable 64-bit DMA addresses\n",
909 PvScsiRestorePciAttributes (
913 Dev
->PciIo
->Attributes (
915 EfiPciIoAttributeOperationSet
,
916 Dev
->OriginalPciAttributes
,
923 PvScsiAllocateSharedPages (
926 OUT VOID
**HostAddress
,
927 OUT PVSCSI_DMA_DESC
*DmaDesc
933 Status
= Dev
->PciIo
->AllocateBuffer (
939 EFI_PCI_ATTRIBUTE_MEMORY_CACHED
941 if (EFI_ERROR (Status
)) {
945 NumberOfBytes
= EFI_PAGES_TO_SIZE (Pages
);
946 Status
= Dev
->PciIo
->Map (
948 EfiPciIoOperationBusMasterCommonBuffer
,
951 &DmaDesc
->DeviceAddress
,
954 if (EFI_ERROR (Status
)) {
958 if (NumberOfBytes
!= EFI_PAGES_TO_SIZE (Pages
)) {
959 Status
= EFI_OUT_OF_RESOURCES
;
966 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
969 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, *HostAddress
);
976 PvScsiFreeSharedPages (
979 IN VOID
*HostAddress
,
980 IN PVSCSI_DMA_DESC
*DmaDesc
983 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
984 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, HostAddress
);
990 IN OUT PVSCSI_DEV
*Dev
995 PVSCSI_CMD_DESC_SETUP_RINGS Cmd
;
998 PVSCSI_CMD_DESC_SETUP_RINGS
*Cmd
;
1000 Cmd
= &AlignedCmd
.Cmd
;
1002 Status
= PvScsiAllocateSharedPages (
1005 (VOID
**)&Dev
->RingDesc
.RingState
,
1006 &Dev
->RingDesc
.RingStateDmaDesc
1008 if (EFI_ERROR (Status
)) {
1011 ZeroMem (Dev
->RingDesc
.RingState
, EFI_PAGE_SIZE
);
1013 Status
= PvScsiAllocateSharedPages (
1016 (VOID
**)&Dev
->RingDesc
.RingReqs
,
1017 &Dev
->RingDesc
.RingReqsDmaDesc
1019 if (EFI_ERROR (Status
)) {
1022 ZeroMem (Dev
->RingDesc
.RingReqs
, EFI_PAGE_SIZE
);
1024 Status
= PvScsiAllocateSharedPages (
1027 (VOID
**)&Dev
->RingDesc
.RingCmps
,
1028 &Dev
->RingDesc
.RingCmpsDmaDesc
1030 if (EFI_ERROR (Status
)) {
1033 ZeroMem (Dev
->RingDesc
.RingCmps
, EFI_PAGE_SIZE
);
1035 ZeroMem (Cmd
, sizeof (*Cmd
));
1036 Cmd
->ReqRingNumPages
= 1;
1037 Cmd
->CmpRingNumPages
= 1;
1038 Cmd
->RingsStatePPN
= RShiftU64 (
1039 Dev
->RingDesc
.RingStateDmaDesc
.DeviceAddress
,
1042 Cmd
->ReqRingPPNs
[0] = RShiftU64 (
1043 Dev
->RingDesc
.RingReqsDmaDesc
.DeviceAddress
,
1046 Cmd
->CmpRingPPNs
[0] = RShiftU64 (
1047 Dev
->RingDesc
.RingCmpsDmaDesc
.DeviceAddress
,
1052 sizeof (*Cmd
) % sizeof (UINT32
) == 0,
1053 "Cmd must be multiple of 32-bit words"
1055 Status
= PvScsiWriteCmdDesc (
1057 PvScsiCmdSetupRings
,
1059 sizeof (*Cmd
) / sizeof (UINT32
)
1061 if (EFI_ERROR (Status
)) {
1068 PvScsiFreeSharedPages (
1071 Dev
->RingDesc
.RingCmps
,
1072 &Dev
->RingDesc
.RingCmpsDmaDesc
1076 PvScsiFreeSharedPages (
1079 Dev
->RingDesc
.RingReqs
,
1080 &Dev
->RingDesc
.RingReqsDmaDesc
1084 PvScsiFreeSharedPages (
1087 Dev
->RingDesc
.RingState
,
1088 &Dev
->RingDesc
.RingStateDmaDesc
1097 IN OUT PVSCSI_DEV
*Dev
1100 PvScsiFreeSharedPages (
1103 Dev
->RingDesc
.RingCmps
,
1104 &Dev
->RingDesc
.RingCmpsDmaDesc
1107 PvScsiFreeSharedPages (
1110 Dev
->RingDesc
.RingReqs
,
1111 &Dev
->RingDesc
.RingReqsDmaDesc
1114 PvScsiFreeSharedPages (
1117 Dev
->RingDesc
.RingState
,
1118 &Dev
->RingDesc
.RingStateDmaDesc
1125 IN OUT PVSCSI_DEV
*Dev
1131 // Init configuration
1133 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
1134 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
1135 Dev
->WaitForCmpStallInUsecs
= PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs
);
1138 // Set PCI Attributes
1140 Status
= PvScsiSetPciAttributes (Dev
);
1141 if (EFI_ERROR (Status
)) {
1148 Status
= PvScsiResetAdapter (Dev
);
1149 if (EFI_ERROR (Status
)) {
1150 goto RestorePciAttributes
;
1154 // Init PVSCSI rings
1156 Status
= PvScsiInitRings (Dev
);
1157 if (EFI_ERROR (Status
)) {
1158 goto RestorePciAttributes
;
1162 // Allocate DMA communication buffer
1164 Status
= PvScsiAllocateSharedPages (
1166 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1167 (VOID
**)&Dev
->DmaBuf
,
1170 if (EFI_ERROR (Status
)) {
1175 // Populate the exported interface's attributes
1177 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1178 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
1179 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
1180 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
1181 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
1182 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
1183 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
1184 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
1187 // AdapterId is a target for which no handle will be created during bus scan.
1188 // Prevent any conflict with real devices.
1190 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
1193 // Set both physical and logical attributes for non-RAID SCSI channel
1195 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1196 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1199 // No restriction on transfer buffer alignment
1201 Dev
->PassThruMode
.IoAlign
= 0;
1207 // Reset device to stop device usage of the rings.
1208 // This is required to safely free the rings.
1210 PvScsiResetAdapter (Dev
);
1212 PvScsiFreeRings (Dev
);
1214 RestorePciAttributes
:
1215 PvScsiRestorePciAttributes (Dev
);
1223 IN OUT PVSCSI_DEV
*Dev
1228 // - Make device stop processing all requests.
1229 // - Stop device usage of the rings.
1231 // This is required to safely free the DMA communication buffer
1234 PvScsiResetAdapter (Dev
);
1237 // Free DMA communication buffer
1239 PvScsiFreeSharedPages (
1241 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1246 PvScsiFreeRings (Dev
);
1248 PvScsiRestorePciAttributes (Dev
);
1252 Event notification called by ExitBootServices()
1265 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1268 // Reset the device to stop device usage of the rings.
1270 // We allocated said rings in EfiBootServicesData type memory, and code
1271 // executing after ExitBootServices() is permitted to overwrite it.
1273 PvScsiResetAdapter (Dev
);
1283 PvScsiDriverBindingSupported (
1284 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1285 IN EFI_HANDLE ControllerHandle
,
1286 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1290 EFI_PCI_IO_PROTOCOL
*PciIo
;
1293 Status
= gBS
->OpenProtocol (
1295 &gEfiPciIoProtocolGuid
,
1297 This
->DriverBindingHandle
,
1299 EFI_OPEN_PROTOCOL_BY_DRIVER
1301 if (EFI_ERROR (Status
)) {
1305 Status
= PciIo
->Pci
.Read (
1307 EfiPciIoWidthUint32
,
1309 sizeof (Pci
) / sizeof (UINT32
),
1312 if (EFI_ERROR (Status
)) {
1316 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
1317 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
1318 Status
= EFI_UNSUPPORTED
;
1322 Status
= EFI_SUCCESS
;
1325 gBS
->CloseProtocol (
1327 &gEfiPciIoProtocolGuid
,
1328 This
->DriverBindingHandle
,
1338 PvScsiDriverBindingStart (
1339 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1340 IN EFI_HANDLE ControllerHandle
,
1341 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1347 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
1349 return EFI_OUT_OF_RESOURCES
;
1352 Status
= gBS
->OpenProtocol (
1354 &gEfiPciIoProtocolGuid
,
1355 (VOID
**)&Dev
->PciIo
,
1356 This
->DriverBindingHandle
,
1358 EFI_OPEN_PROTOCOL_BY_DRIVER
1360 if (EFI_ERROR (Status
)) {
1364 Status
= PvScsiInit (Dev
);
1365 if (EFI_ERROR (Status
)) {
1369 Status
= gBS
->CreateEvent (
1370 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
1376 if (EFI_ERROR (Status
)) {
1381 // Setup complete, attempt to export the driver instance's PassThru interface
1383 Dev
->Signature
= PVSCSI_SIG
;
1384 Status
= gBS
->InstallProtocolInterface (
1386 &gEfiExtScsiPassThruProtocolGuid
,
1387 EFI_NATIVE_INTERFACE
,
1390 if (EFI_ERROR (Status
)) {
1397 gBS
->CloseEvent (Dev
->ExitBoot
);
1403 gBS
->CloseProtocol (
1405 &gEfiPciIoProtocolGuid
,
1406 This
->DriverBindingHandle
,
1419 PvScsiDriverBindingStop (
1420 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1421 IN EFI_HANDLE ControllerHandle
,
1422 IN UINTN NumberOfChildren
,
1423 IN EFI_HANDLE
*ChildHandleBuffer
1427 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1430 Status
= gBS
->OpenProtocol (
1432 &gEfiExtScsiPassThruProtocolGuid
,
1434 This
->DriverBindingHandle
,
1436 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
1438 if (EFI_ERROR (Status
)) {
1442 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
1444 Status
= gBS
->UninstallProtocolInterface (
1446 &gEfiExtScsiPassThruProtocolGuid
,
1449 if (EFI_ERROR (Status
)) {
1453 gBS
->CloseEvent (Dev
->ExitBoot
);
1457 gBS
->CloseProtocol (
1459 &gEfiPciIoProtocolGuid
,
1460 This
->DriverBindingHandle
,
1469 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
1470 &PvScsiDriverBindingSupported
,
1471 &PvScsiDriverBindingStart
,
1472 &PvScsiDriverBindingStop
,
1473 PVSCSI_BINDING_VERSION
,
1474 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
1475 NULL
// DriverBindingHandle, filled as well
1482 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1483 { "eng;en", L
"PVSCSI Host Driver" },
1487 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
1492 PvScsiGetDriverName (
1493 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1495 OUT CHAR16
**DriverName
1498 return LookupUnicodeString2 (
1500 This
->SupportedLanguages
,
1503 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
1510 PvScsiGetDeviceName (
1511 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1512 IN EFI_HANDLE DeviceHandle
,
1513 IN EFI_HANDLE ChildHandle
,
1515 OUT CHAR16
**ControllerName
1518 return EFI_UNSUPPORTED
;
1521 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
1522 &PvScsiGetDriverName
,
1523 &PvScsiGetDeviceName
,
1524 "eng" // SupportedLanguages, ISO 639-2 language codes
1527 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
1528 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
1529 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
1530 "en" // SupportedLanguages, RFC 4646 language codes
1540 IN EFI_HANDLE ImageHandle
,
1541 IN EFI_SYSTEM_TABLE
*SystemTable
1544 return EfiLibInstallDriverBindingComponentName2 (
1547 &mPvScsiDriverBinding
,