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
)) {
878 // Signal device supports 64-bit DMA addresses
880 Status
= Dev
->PciIo
->Attributes (
882 EfiPciIoAttributeOperationEnable
,
883 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE
,
886 if (EFI_ERROR (Status
)) {
888 // Warn user that device will only be using 32-bit DMA addresses.
890 // Note that this does not prevent the device/driver from working
891 // and therefore we only warn and continue as usual.
895 "%a: failed to enable 64-bit DMA addresses\n",
905 PvScsiRestorePciAttributes (
909 Dev
->PciIo
->Attributes (
911 EfiPciIoAttributeOperationSet
,
912 Dev
->OriginalPciAttributes
,
919 PvScsiAllocateSharedPages (
922 OUT VOID
**HostAddress
,
923 OUT PVSCSI_DMA_DESC
*DmaDesc
929 Status
= Dev
->PciIo
->AllocateBuffer (
935 EFI_PCI_ATTRIBUTE_MEMORY_CACHED
937 if (EFI_ERROR (Status
)) {
941 NumberOfBytes
= EFI_PAGES_TO_SIZE (Pages
);
942 Status
= Dev
->PciIo
->Map (
944 EfiPciIoOperationBusMasterCommonBuffer
,
947 &DmaDesc
->DeviceAddress
,
950 if (EFI_ERROR (Status
)) {
954 if (NumberOfBytes
!= EFI_PAGES_TO_SIZE (Pages
)) {
955 Status
= EFI_OUT_OF_RESOURCES
;
962 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
965 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, *HostAddress
);
972 PvScsiFreeSharedPages (
975 IN VOID
*HostAddress
,
976 IN PVSCSI_DMA_DESC
*DmaDesc
979 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
980 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, HostAddress
);
986 IN OUT PVSCSI_DEV
*Dev
991 PVSCSI_CMD_DESC_SETUP_RINGS Cmd
;
994 PVSCSI_CMD_DESC_SETUP_RINGS
*Cmd
;
996 Cmd
= &AlignedCmd
.Cmd
;
998 Status
= PvScsiAllocateSharedPages (
1001 (VOID
**)&Dev
->RingDesc
.RingState
,
1002 &Dev
->RingDesc
.RingStateDmaDesc
1004 if (EFI_ERROR (Status
)) {
1007 ZeroMem (Dev
->RingDesc
.RingState
, EFI_PAGE_SIZE
);
1009 Status
= PvScsiAllocateSharedPages (
1012 (VOID
**)&Dev
->RingDesc
.RingReqs
,
1013 &Dev
->RingDesc
.RingReqsDmaDesc
1015 if (EFI_ERROR (Status
)) {
1018 ZeroMem (Dev
->RingDesc
.RingReqs
, EFI_PAGE_SIZE
);
1020 Status
= PvScsiAllocateSharedPages (
1023 (VOID
**)&Dev
->RingDesc
.RingCmps
,
1024 &Dev
->RingDesc
.RingCmpsDmaDesc
1026 if (EFI_ERROR (Status
)) {
1029 ZeroMem (Dev
->RingDesc
.RingCmps
, EFI_PAGE_SIZE
);
1031 ZeroMem (Cmd
, sizeof (*Cmd
));
1032 Cmd
->ReqRingNumPages
= 1;
1033 Cmd
->CmpRingNumPages
= 1;
1034 Cmd
->RingsStatePPN
= RShiftU64 (
1035 Dev
->RingDesc
.RingStateDmaDesc
.DeviceAddress
,
1038 Cmd
->ReqRingPPNs
[0] = RShiftU64 (
1039 Dev
->RingDesc
.RingReqsDmaDesc
.DeviceAddress
,
1042 Cmd
->CmpRingPPNs
[0] = RShiftU64 (
1043 Dev
->RingDesc
.RingCmpsDmaDesc
.DeviceAddress
,
1048 sizeof (*Cmd
) % sizeof (UINT32
) == 0,
1049 "Cmd must be multiple of 32-bit words"
1051 Status
= PvScsiWriteCmdDesc (
1053 PvScsiCmdSetupRings
,
1055 sizeof (*Cmd
) / sizeof (UINT32
)
1057 if (EFI_ERROR (Status
)) {
1064 PvScsiFreeSharedPages (
1067 Dev
->RingDesc
.RingCmps
,
1068 &Dev
->RingDesc
.RingCmpsDmaDesc
1072 PvScsiFreeSharedPages (
1075 Dev
->RingDesc
.RingReqs
,
1076 &Dev
->RingDesc
.RingReqsDmaDesc
1080 PvScsiFreeSharedPages (
1083 Dev
->RingDesc
.RingState
,
1084 &Dev
->RingDesc
.RingStateDmaDesc
1093 IN OUT PVSCSI_DEV
*Dev
1096 PvScsiFreeSharedPages (
1099 Dev
->RingDesc
.RingCmps
,
1100 &Dev
->RingDesc
.RingCmpsDmaDesc
1103 PvScsiFreeSharedPages (
1106 Dev
->RingDesc
.RingReqs
,
1107 &Dev
->RingDesc
.RingReqsDmaDesc
1110 PvScsiFreeSharedPages (
1113 Dev
->RingDesc
.RingState
,
1114 &Dev
->RingDesc
.RingStateDmaDesc
1121 IN OUT PVSCSI_DEV
*Dev
1127 // Init configuration
1129 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
1130 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
1131 Dev
->WaitForCmpStallInUsecs
= PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs
);
1134 // Set PCI Attributes
1136 Status
= PvScsiSetPciAttributes (Dev
);
1137 if (EFI_ERROR (Status
)) {
1144 Status
= PvScsiResetAdapter (Dev
);
1145 if (EFI_ERROR (Status
)) {
1146 goto RestorePciAttributes
;
1150 // Init PVSCSI rings
1152 Status
= PvScsiInitRings (Dev
);
1153 if (EFI_ERROR (Status
)) {
1154 goto RestorePciAttributes
;
1158 // Allocate DMA communication buffer
1160 Status
= PvScsiAllocateSharedPages (
1162 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1163 (VOID
**)&Dev
->DmaBuf
,
1166 if (EFI_ERROR (Status
)) {
1171 // Populate the exported interface's attributes
1173 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
1174 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
1175 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
1176 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
1177 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
1178 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
1179 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
1180 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
1183 // AdapterId is a target for which no handle will be created during bus scan.
1184 // Prevent any conflict with real devices.
1186 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
1189 // Set both physical and logical attributes for non-RAID SCSI channel
1191 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
1192 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
1195 // No restriction on transfer buffer alignment
1197 Dev
->PassThruMode
.IoAlign
= 0;
1203 // Reset device to stop device usage of the rings.
1204 // This is required to safely free the rings.
1206 PvScsiResetAdapter (Dev
);
1208 PvScsiFreeRings (Dev
);
1210 RestorePciAttributes
:
1211 PvScsiRestorePciAttributes (Dev
);
1219 IN OUT PVSCSI_DEV
*Dev
1224 // - Make device stop processing all requests.
1225 // - Stop device usage of the rings.
1227 // This is required to safely free the DMA communication buffer
1230 PvScsiResetAdapter (Dev
);
1233 // Free DMA communication buffer
1235 PvScsiFreeSharedPages (
1237 EFI_SIZE_TO_PAGES (sizeof (*Dev
->DmaBuf
)),
1242 PvScsiFreeRings (Dev
);
1244 PvScsiRestorePciAttributes (Dev
);
1248 Event notification called by ExitBootServices()
1261 DEBUG ((DEBUG_VERBOSE
, "%a: Context=0x%p\n", __FUNCTION__
, Context
));
1264 // Reset the device to stop device usage of the rings.
1266 // We allocated said rings in EfiBootServicesData type memory, and code
1267 // executing after ExitBootServices() is permitted to overwrite it.
1269 PvScsiResetAdapter (Dev
);
1279 PvScsiDriverBindingSupported (
1280 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1281 IN EFI_HANDLE ControllerHandle
,
1282 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1286 EFI_PCI_IO_PROTOCOL
*PciIo
;
1289 Status
= gBS
->OpenProtocol (
1291 &gEfiPciIoProtocolGuid
,
1293 This
->DriverBindingHandle
,
1295 EFI_OPEN_PROTOCOL_BY_DRIVER
1297 if (EFI_ERROR (Status
)) {
1301 Status
= PciIo
->Pci
.Read (
1303 EfiPciIoWidthUint32
,
1305 sizeof (Pci
) / sizeof (UINT32
),
1308 if (EFI_ERROR (Status
)) {
1312 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
1313 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
1314 Status
= EFI_UNSUPPORTED
;
1318 Status
= EFI_SUCCESS
;
1321 gBS
->CloseProtocol (
1323 &gEfiPciIoProtocolGuid
,
1324 This
->DriverBindingHandle
,
1334 PvScsiDriverBindingStart (
1335 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1336 IN EFI_HANDLE ControllerHandle
,
1337 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
1343 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
1345 return EFI_OUT_OF_RESOURCES
;
1348 Status
= gBS
->OpenProtocol (
1350 &gEfiPciIoProtocolGuid
,
1351 (VOID
**)&Dev
->PciIo
,
1352 This
->DriverBindingHandle
,
1354 EFI_OPEN_PROTOCOL_BY_DRIVER
1356 if (EFI_ERROR (Status
)) {
1360 Status
= PvScsiInit (Dev
);
1361 if (EFI_ERROR (Status
)) {
1365 Status
= gBS
->CreateEvent (
1366 EVT_SIGNAL_EXIT_BOOT_SERVICES
,
1372 if (EFI_ERROR (Status
)) {
1377 // Setup complete, attempt to export the driver instance's PassThru interface
1379 Dev
->Signature
= PVSCSI_SIG
;
1380 Status
= gBS
->InstallProtocolInterface (
1382 &gEfiExtScsiPassThruProtocolGuid
,
1383 EFI_NATIVE_INTERFACE
,
1386 if (EFI_ERROR (Status
)) {
1393 gBS
->CloseEvent (Dev
->ExitBoot
);
1399 gBS
->CloseProtocol (
1401 &gEfiPciIoProtocolGuid
,
1402 This
->DriverBindingHandle
,
1415 PvScsiDriverBindingStop (
1416 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
1417 IN EFI_HANDLE ControllerHandle
,
1418 IN UINTN NumberOfChildren
,
1419 IN EFI_HANDLE
*ChildHandleBuffer
1423 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
1426 Status
= gBS
->OpenProtocol (
1428 &gEfiExtScsiPassThruProtocolGuid
,
1430 This
->DriverBindingHandle
,
1432 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
1434 if (EFI_ERROR (Status
)) {
1438 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
1440 Status
= gBS
->UninstallProtocolInterface (
1442 &gEfiExtScsiPassThruProtocolGuid
,
1445 if (EFI_ERROR (Status
)) {
1449 gBS
->CloseEvent (Dev
->ExitBoot
);
1453 gBS
->CloseProtocol (
1455 &gEfiPciIoProtocolGuid
,
1456 This
->DriverBindingHandle
,
1465 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
1466 &PvScsiDriverBindingSupported
,
1467 &PvScsiDriverBindingStart
,
1468 &PvScsiDriverBindingStop
,
1469 PVSCSI_BINDING_VERSION
,
1470 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
1471 NULL
// DriverBindingHandle, filled as well
1478 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
1479 { "eng;en", L
"PVSCSI Host Driver" },
1483 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
1488 PvScsiGetDriverName (
1489 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1491 OUT CHAR16
**DriverName
1494 return LookupUnicodeString2 (
1496 This
->SupportedLanguages
,
1499 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
1506 PvScsiGetDeviceName (
1507 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
1508 IN EFI_HANDLE DeviceHandle
,
1509 IN EFI_HANDLE ChildHandle
,
1511 OUT CHAR16
**ControllerName
1514 return EFI_UNSUPPORTED
;
1517 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
1518 &PvScsiGetDriverName
,
1519 &PvScsiGetDeviceName
,
1520 "eng" // SupportedLanguages, ISO 639-2 language codes
1523 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
1524 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
1525 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
1526 "en" // SupportedLanguages, RFC 4646 language codes
1536 IN EFI_HANDLE ImageHandle
,
1537 IN EFI_SYSTEM_TABLE
*SystemTable
1540 return EfiLibInstallDriverBindingComponentName2 (
1543 &mPvScsiDriverBinding
,