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 Status
= PvScsiAllocateSharedPages (
998 (VOID
**)&Dev
->RingDesc
.RingState
,
999 &Dev
->RingDesc
.RingStateDmaDesc
1001 if (EFI_ERROR (Status
)) {
1004 ZeroMem (Dev
->RingDesc
.RingState
, EFI_PAGE_SIZE
);
1006 Status
= PvScsiAllocateSharedPages (
1009 (VOID
**)&Dev
->RingDesc
.RingReqs
,
1010 &Dev
->RingDesc
.RingReqsDmaDesc
1012 if (EFI_ERROR (Status
)) {
1015 ZeroMem (Dev
->RingDesc
.RingReqs
, EFI_PAGE_SIZE
);
1017 Status
= PvScsiAllocateSharedPages (
1020 (VOID
**)&Dev
->RingDesc
.RingCmps
,
1021 &Dev
->RingDesc
.RingCmpsDmaDesc
1023 if (EFI_ERROR (Status
)) {
1026 ZeroMem (Dev
->RingDesc
.RingCmps
, EFI_PAGE_SIZE
);
1031 PvScsiFreeSharedPages (
1034 Dev
->RingDesc
.RingReqs
,
1035 &Dev
->RingDesc
.RingReqsDmaDesc
1039 PvScsiFreeSharedPages (
1042 Dev
->RingDesc
.RingState
,
1043 &Dev
->RingDesc
.RingStateDmaDesc
1052 IN OUT PVSCSI_DEV
*Dev
1055 PvScsiFreeSharedPages (
1058 Dev
->RingDesc
.RingCmps
,
1059 &Dev
->RingDesc
.RingCmpsDmaDesc
1062 PvScsiFreeSharedPages (
1065 Dev
->RingDesc
.RingReqs
,
1066 &Dev
->RingDesc
.RingReqsDmaDesc
1069 PvScsiFreeSharedPages (
1072 Dev
->RingDesc
.RingState
,
1073 &Dev
->RingDesc
.RingStateDmaDesc
1080 IN OUT PVSCSI_DEV
*Dev
1084 PVSCSI_CMD_DESC_SETUP_RINGS Cmd
;
1087 PVSCSI_CMD_DESC_SETUP_RINGS
*Cmd
;
1089 Cmd
= &AlignedCmd
.Cmd
;
1091 ZeroMem (Cmd
, sizeof (*Cmd
));
1092 Cmd
->ReqRingNumPages
= 1;
1093 Cmd
->CmpRingNumPages
= 1;
1094 Cmd
->RingsStatePPN
= RShiftU64 (
1095 Dev
->RingDesc
.RingStateDmaDesc
.DeviceAddress
,
1098 Cmd
->ReqRingPPNs
[0] = RShiftU64 (
1099 Dev
->RingDesc
.RingReqsDmaDesc
.DeviceAddress
,
1102 Cmd
->CmpRingPPNs
[0] = RShiftU64 (
1103 Dev
->RingDesc
.RingCmpsDmaDesc
.DeviceAddress
,
1108 sizeof (*Cmd
) % sizeof (UINT32
) == 0,
1109 "Cmd must be multiple of 32-bit words"
1111 return PvScsiWriteCmdDesc (
1113 PvScsiCmdSetupRings
,
1115 sizeof (*Cmd
) / sizeof (UINT32
)
1122 IN OUT PVSCSI_DEV
*Dev
1128 // Init configuration
1130 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
1131 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
1132 Dev
->WaitForCmpStallInUsecs
= PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs
);
1135 // Set PCI Attributes
1137 Status
= PvScsiSetPciAttributes (Dev
);
1138 if (EFI_ERROR (Status
)) {
1145 Status
= PvScsiResetAdapter (Dev
);
1146 if (EFI_ERROR (Status
)) {
1147 goto RestorePciAttributes
;
1151 // Init PVSCSI rings
1153 Status
= PvScsiInitRings (Dev
);
1154 if (EFI_ERROR (Status
)) {
1155 goto RestorePciAttributes
;
1159 // Allocate DMA communication buffer
1161 Status
= PvScsiAllocateSharedPages (
1163 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1164 (VOID
**)&Dev
->DmaBuf
,
1167 if (EFI_ERROR (Status
)) {
1172 // Setup rings against device
1174 Status
= PvScsiSetupRings (Dev
);
1175 if (EFI_ERROR (Status
)) {
1176 goto FreeDmaCommBuffer
;
1180 // Populate the exported interface's attributes
1182 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1183 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
1184 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
1185 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
1186 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
1187 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
1188 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
1189 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
1192 // AdapterId is a target for which no handle will be created during bus scan.
1193 // Prevent any conflict with real devices.
1195 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
1198 // Set both physical and logical attributes for non-RAID SCSI channel
1200 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1201 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1204 // No restriction on transfer buffer alignment
1206 Dev
->PassThruMode
.IoAlign
= 0;
1211 PvScsiFreeSharedPages (
1213 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1219 PvScsiFreeRings (Dev
);
1221 RestorePciAttributes
:
1222 PvScsiRestorePciAttributes (Dev
);
1230 IN OUT PVSCSI_DEV
*Dev
1235 // - Make device stop processing all requests.
1236 // - Stop device usage of the rings.
1238 // This is required to safely free the DMA communication buffer
1241 PvScsiResetAdapter (Dev
);
1244 // Free DMA communication buffer
1246 PvScsiFreeSharedPages (
1248 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1253 PvScsiFreeRings (Dev
);
1255 PvScsiRestorePciAttributes (Dev
);
1259 Event notification called by ExitBootServices()
1272 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1275 // Reset the device to stop device usage of the rings.
1277 // We allocated said rings in EfiBootServicesData type memory, and code
1278 // executing after ExitBootServices() is permitted to overwrite it.
1280 PvScsiResetAdapter (Dev
);
1290 PvScsiDriverBindingSupported (
1291 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1292 IN EFI_HANDLE ControllerHandle
,
1293 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1297 EFI_PCI_IO_PROTOCOL
*PciIo
;
1300 Status
= gBS
->OpenProtocol (
1302 &gEfiPciIoProtocolGuid
,
1304 This
->DriverBindingHandle
,
1306 EFI_OPEN_PROTOCOL_BY_DRIVER
1308 if (EFI_ERROR (Status
)) {
1312 Status
= PciIo
->Pci
.Read (
1314 EfiPciIoWidthUint32
,
1316 sizeof (Pci
) / sizeof (UINT32
),
1319 if (EFI_ERROR (Status
)) {
1323 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
1324 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
1325 Status
= EFI_UNSUPPORTED
;
1329 Status
= EFI_SUCCESS
;
1332 gBS
->CloseProtocol (
1334 &gEfiPciIoProtocolGuid
,
1335 This
->DriverBindingHandle
,
1345 PvScsiDriverBindingStart (
1346 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1347 IN EFI_HANDLE ControllerHandle
,
1348 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1354 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
1356 return EFI_OUT_OF_RESOURCES
;
1359 Status
= gBS
->OpenProtocol (
1361 &gEfiPciIoProtocolGuid
,
1362 (VOID
**)&Dev
->PciIo
,
1363 This
->DriverBindingHandle
,
1365 EFI_OPEN_PROTOCOL_BY_DRIVER
1367 if (EFI_ERROR (Status
)) {
1371 Status
= PvScsiInit (Dev
);
1372 if (EFI_ERROR (Status
)) {
1376 Status
= gBS
->CreateEvent (
1377 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
1383 if (EFI_ERROR (Status
)) {
1388 // Setup complete, attempt to export the driver instance's PassThru interface
1390 Dev
->Signature
= PVSCSI_SIG
;
1391 Status
= gBS
->InstallProtocolInterface (
1393 &gEfiExtScsiPassThruProtocolGuid
,
1394 EFI_NATIVE_INTERFACE
,
1397 if (EFI_ERROR (Status
)) {
1404 gBS
->CloseEvent (Dev
->ExitBoot
);
1410 gBS
->CloseProtocol (
1412 &gEfiPciIoProtocolGuid
,
1413 This
->DriverBindingHandle
,
1426 PvScsiDriverBindingStop (
1427 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1428 IN EFI_HANDLE ControllerHandle
,
1429 IN UINTN NumberOfChildren
,
1430 IN EFI_HANDLE
*ChildHandleBuffer
1434 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1437 Status
= gBS
->OpenProtocol (
1439 &gEfiExtScsiPassThruProtocolGuid
,
1441 This
->DriverBindingHandle
,
1443 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
1445 if (EFI_ERROR (Status
)) {
1449 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
1451 Status
= gBS
->UninstallProtocolInterface (
1453 &gEfiExtScsiPassThruProtocolGuid
,
1456 if (EFI_ERROR (Status
)) {
1460 gBS
->CloseEvent (Dev
->ExitBoot
);
1464 gBS
->CloseProtocol (
1466 &gEfiPciIoProtocolGuid
,
1467 This
->DriverBindingHandle
,
1476 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
1477 &PvScsiDriverBindingSupported
,
1478 &PvScsiDriverBindingStart
,
1479 &PvScsiDriverBindingStop
,
1480 PVSCSI_BINDING_VERSION
,
1481 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
1482 NULL
// DriverBindingHandle, filled as well
1489 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1490 { "eng;en", L
"PVSCSI Host Driver" },
1494 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
1499 PvScsiGetDriverName (
1500 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1502 OUT CHAR16
**DriverName
1505 return LookupUnicodeString2 (
1507 This
->SupportedLanguages
,
1510 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
1517 PvScsiGetDeviceName (
1518 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1519 IN EFI_HANDLE DeviceHandle
,
1520 IN EFI_HANDLE ChildHandle
,
1522 OUT CHAR16
**ControllerName
1525 return EFI_UNSUPPORTED
;
1528 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
1529 &PvScsiGetDriverName
,
1530 &PvScsiGetDeviceName
,
1531 "eng" // SupportedLanguages, ISO 639-2 language codes
1534 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
1535 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
1536 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
1537 "en" // SupportedLanguages, RFC 4646 language codes
1547 IN EFI_HANDLE ImageHandle
,
1548 IN EFI_SYSTEM_TABLE
*SystemTable
1551 return EfiLibInstallDriverBindingComponentName2 (
1554 &mPvScsiDriverBinding
,