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 Writes a 32-bit value into BAR0 using MMIO
41 IN CONST PVSCSI_DEV
*Dev
,
46 return Dev
->PciIo
->Mem
.Write (
57 Writes multiple words of data into BAR0 using MMIO
61 PvScsiMmioWrite32Multiple (
62 IN CONST PVSCSI_DEV
*Dev
,
68 return Dev
->PciIo
->Mem
.Write (
70 EfiPciIoWidthFifoUint32
,
79 Send a PVSCSI command to device.
81 @param[in] Dev The pvscsi host device.
82 @param[in] Cmd The command to send to device.
83 @param[in] OPTIONAL DescWords An optional command descriptor (If command
84 have a descriptor). The descriptor is
85 provided as an array of UINT32 words and
86 is must be 32-bit aligned.
87 @param[in] DescWordsCount The number of words in command descriptor.
88 Caller must specify here 0 if DescWords
89 is not supplied (It is optional). In that
90 case, DescWords is ignored.
92 @return Status codes returned by Dev->PciIo->Mem.Write().
98 IN CONST PVSCSI_DEV
*Dev
,
100 IN UINT32
*DescWords OPTIONAL
,
101 IN UINTN DescWordsCount
106 if (DescWordsCount
> PVSCSI_MAX_CMD_DATA_WORDS
) {
107 return EFI_INVALID_PARAMETER
;
110 Status
= PvScsiMmioWrite32 (Dev
, PvScsiRegOffsetCommand
, Cmd
);
111 if (EFI_ERROR (Status
)) {
115 if (DescWordsCount
> 0) {
116 return PvScsiMmioWrite32Multiple (
118 PvScsiRegOffsetCommandData
,
130 IN CONST PVSCSI_DEV
*Dev
133 return PvScsiWriteCmdDesc (Dev
, PvScsiCmdAdapterReset
, NULL
, 0);
137 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
138 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
142 IsTargetInitialized (
148 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
149 if (Target
[Idx
] != 0xFF) {
157 // Ext SCSI Pass Thru
164 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
167 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
168 IN EFI_EVENT Event OPTIONAL
171 return EFI_UNSUPPORTED
;
177 PvScsiGetNextTargetLun (
178 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
179 IN OUT UINT8
**Target
,
187 if (Target
== NULL
) {
188 return EFI_INVALID_PARAMETER
;
192 // The Target input parameter is unnecessarily a pointer-to-pointer
197 // If target not initialized, return first target & LUN
199 if (!IsTargetInitialized (TargetPtr
)) {
200 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
206 // We only use first byte of target identifer
208 LastTarget
= *TargetPtr
;
211 // Increment (target, LUN) pair if valid on input
213 Dev
= PVSCSI_FROM_PASS_THRU (This
);
214 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
215 return EFI_INVALID_PARAMETER
;
218 if (*Lun
< Dev
->MaxLun
) {
223 if (LastTarget
< Dev
->MaxTarget
) {
226 *TargetPtr
= LastTarget
;
230 return EFI_NOT_FOUND
;
236 PvScsiBuildDevicePath (
237 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
240 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
245 SCSI_DEVICE_PATH
*ScsiDevicePath
;
247 if (DevicePath
== NULL
) {
248 return EFI_INVALID_PARAMETER
;
252 // We only use first byte of target identifer
254 TargetValue
= *Target
;
256 Dev
= PVSCSI_FROM_PASS_THRU (This
);
257 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
) {
258 return EFI_NOT_FOUND
;
261 ScsiDevicePath
= AllocatePool (sizeof (*ScsiDevicePath
));
262 if (ScsiDevicePath
== NULL
) {
263 return EFI_OUT_OF_RESOURCES
;
266 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
267 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
268 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
269 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
270 ScsiDevicePath
->Pun
= TargetValue
;
271 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
273 *DevicePath
= &ScsiDevicePath
->Header
;
281 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
282 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
287 SCSI_DEVICE_PATH
*ScsiDevicePath
;
290 if (DevicePath
== NULL
|| Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
291 return EFI_INVALID_PARAMETER
;
294 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
295 DevicePath
->SubType
!= MSG_SCSI_DP
) {
296 return EFI_UNSUPPORTED
;
299 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
300 Dev
= PVSCSI_FROM_PASS_THRU (This
);
301 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
302 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
303 return EFI_NOT_FOUND
;
307 // We only use first byte of target identifer
309 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
310 ZeroMem (*Target
+ 1, TARGET_MAX_BYTES
- 1);
311 *Lun
= ScsiDevicePath
->Lun
;
320 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
323 return EFI_UNSUPPORTED
;
329 PvScsiResetTargetLun (
330 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
335 return EFI_UNSUPPORTED
;
341 PvScsiGetNextTarget (
342 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
343 IN OUT UINT8
**Target
350 if (Target
== NULL
) {
351 return EFI_INVALID_PARAMETER
;
355 // The Target input parameter is unnecessarily a pointer-to-pointer
360 // If target not initialized, return first target
362 if (!IsTargetInitialized (TargetPtr
)) {
363 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
368 // We only use first byte of target identifer
370 LastTarget
= *TargetPtr
;
373 // Increment target if valid on input
375 Dev
= PVSCSI_FROM_PASS_THRU (This
);
376 if (LastTarget
> Dev
->MaxTarget
) {
377 return EFI_INVALID_PARAMETER
;
380 if (LastTarget
< Dev
->MaxTarget
) {
382 *TargetPtr
= LastTarget
;
386 return EFI_NOT_FOUND
;
391 PvScsiSetPciAttributes (
392 IN OUT PVSCSI_DEV
*Dev
398 // Backup original PCI Attributes
400 Status
= Dev
->PciIo
->Attributes (
402 EfiPciIoAttributeOperationGet
,
404 &Dev
->OriginalPciAttributes
406 if (EFI_ERROR (Status
)) {
411 // Enable MMIO-Space & Bus-Mastering
413 Status
= Dev
->PciIo
->Attributes (
415 EfiPciIoAttributeOperationEnable
,
416 (EFI_PCI_IO_ATTRIBUTE_MEMORY
|
417 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER
),
420 if (EFI_ERROR (Status
)) {
429 PvScsiRestorePciAttributes (
433 Dev
->PciIo
->Attributes (
435 EfiPciIoAttributeOperationSet
,
436 Dev
->OriginalPciAttributes
,
443 PvScsiAllocateSharedPages (
446 OUT VOID
**HostAddress
,
447 OUT PVSCSI_DMA_DESC
*DmaDesc
453 Status
= Dev
->PciIo
->AllocateBuffer (
459 EFI_PCI_ATTRIBUTE_MEMORY_CACHED
461 if (EFI_ERROR (Status
)) {
465 NumberOfBytes
= EFI_PAGES_TO_SIZE (Pages
);
466 Status
= Dev
->PciIo
->Map (
468 EfiPciIoOperationBusMasterCommonBuffer
,
471 &DmaDesc
->DeviceAddress
,
474 if (EFI_ERROR (Status
)) {
478 if (NumberOfBytes
!= EFI_PAGES_TO_SIZE (Pages
)) {
479 Status
= EFI_OUT_OF_RESOURCES
;
486 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
489 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, *HostAddress
);
496 PvScsiFreeSharedPages (
499 IN VOID
*HostAddress
,
500 IN PVSCSI_DMA_DESC
*DmaDesc
503 Dev
->PciIo
->Unmap (Dev
->PciIo
, DmaDesc
->Mapping
);
504 Dev
->PciIo
->FreeBuffer (Dev
->PciIo
, Pages
, HostAddress
);
510 IN OUT PVSCSI_DEV
*Dev
515 PVSCSI_CMD_DESC_SETUP_RINGS Cmd
;
518 PVSCSI_CMD_DESC_SETUP_RINGS
*Cmd
;
520 Cmd
= &AlignedCmd
.Cmd
;
522 Status
= PvScsiAllocateSharedPages (
525 (VOID
**)&Dev
->RingDesc
.RingState
,
526 &Dev
->RingDesc
.RingStateDmaDesc
528 if (EFI_ERROR (Status
)) {
531 ZeroMem (Dev
->RingDesc
.RingState
, EFI_PAGE_SIZE
);
533 Status
= PvScsiAllocateSharedPages (
536 (VOID
**)&Dev
->RingDesc
.RingReqs
,
537 &Dev
->RingDesc
.RingReqsDmaDesc
539 if (EFI_ERROR (Status
)) {
542 ZeroMem (Dev
->RingDesc
.RingReqs
, EFI_PAGE_SIZE
);
544 Status
= PvScsiAllocateSharedPages (
547 (VOID
**)&Dev
->RingDesc
.RingCmps
,
548 &Dev
->RingDesc
.RingCmpsDmaDesc
550 if (EFI_ERROR (Status
)) {
553 ZeroMem (Dev
->RingDesc
.RingCmps
, EFI_PAGE_SIZE
);
555 ZeroMem (Cmd
, sizeof (*Cmd
));
556 Cmd
->ReqRingNumPages
= 1;
557 Cmd
->CmpRingNumPages
= 1;
558 Cmd
->RingsStatePPN
= RShiftU64 (
559 Dev
->RingDesc
.RingStateDmaDesc
.DeviceAddress
,
562 Cmd
->ReqRingPPNs
[0] = RShiftU64 (
563 Dev
->RingDesc
.RingReqsDmaDesc
.DeviceAddress
,
566 Cmd
->CmpRingPPNs
[0] = RShiftU64 (
567 Dev
->RingDesc
.RingCmpsDmaDesc
.DeviceAddress
,
572 sizeof (*Cmd
) % sizeof (UINT32
) == 0,
573 "Cmd must be multiple of 32-bit words"
575 Status
= PvScsiWriteCmdDesc (
579 sizeof (*Cmd
) / sizeof (UINT32
)
581 if (EFI_ERROR (Status
)) {
588 PvScsiFreeSharedPages (
591 Dev
->RingDesc
.RingCmps
,
592 &Dev
->RingDesc
.RingCmpsDmaDesc
596 PvScsiFreeSharedPages (
599 Dev
->RingDesc
.RingReqs
,
600 &Dev
->RingDesc
.RingReqsDmaDesc
604 PvScsiFreeSharedPages (
607 Dev
->RingDesc
.RingState
,
608 &Dev
->RingDesc
.RingStateDmaDesc
617 IN OUT PVSCSI_DEV
*Dev
620 PvScsiFreeSharedPages (
623 Dev
->RingDesc
.RingCmps
,
624 &Dev
->RingDesc
.RingCmpsDmaDesc
627 PvScsiFreeSharedPages (
630 Dev
->RingDesc
.RingReqs
,
631 &Dev
->RingDesc
.RingReqsDmaDesc
634 PvScsiFreeSharedPages (
637 Dev
->RingDesc
.RingState
,
638 &Dev
->RingDesc
.RingStateDmaDesc
645 IN OUT PVSCSI_DEV
*Dev
651 // Init configuration
653 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
654 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
657 // Set PCI Attributes
659 Status
= PvScsiSetPciAttributes (Dev
);
660 if (EFI_ERROR (Status
)) {
667 Status
= PvScsiResetAdapter (Dev
);
668 if (EFI_ERROR (Status
)) {
669 goto RestorePciAttributes
;
675 Status
= PvScsiInitRings (Dev
);
676 if (EFI_ERROR (Status
)) {
677 goto RestorePciAttributes
;
681 // Populate the exported interface's attributes
683 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
684 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
685 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
686 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
687 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
688 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
689 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
690 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
693 // AdapterId is a target for which no handle will be created during bus scan.
694 // Prevent any conflict with real devices.
696 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
699 // Set both physical and logical attributes for non-RAID SCSI channel
701 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
702 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
705 // No restriction on transfer buffer alignment
707 Dev
->PassThruMode
.IoAlign
= 0;
711 RestorePciAttributes
:
712 PvScsiRestorePciAttributes (Dev
);
720 IN OUT PVSCSI_DEV
*Dev
724 // Reset device to stop device usage of the rings.
725 // This is required to safely free the rings.
727 PvScsiResetAdapter (Dev
);
729 PvScsiFreeRings (Dev
);
731 PvScsiRestorePciAttributes (Dev
);
741 PvScsiDriverBindingSupported (
742 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
743 IN EFI_HANDLE ControllerHandle
,
744 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
748 EFI_PCI_IO_PROTOCOL
*PciIo
;
751 Status
= gBS
->OpenProtocol (
753 &gEfiPciIoProtocolGuid
,
755 This
->DriverBindingHandle
,
757 EFI_OPEN_PROTOCOL_BY_DRIVER
759 if (EFI_ERROR (Status
)) {
763 Status
= PciIo
->Pci
.Read (
767 sizeof (Pci
) / sizeof (UINT32
),
770 if (EFI_ERROR (Status
)) {
774 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
775 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
776 Status
= EFI_UNSUPPORTED
;
780 Status
= EFI_SUCCESS
;
785 &gEfiPciIoProtocolGuid
,
786 This
->DriverBindingHandle
,
796 PvScsiDriverBindingStart (
797 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
798 IN EFI_HANDLE ControllerHandle
,
799 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
805 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
807 return EFI_OUT_OF_RESOURCES
;
810 Status
= gBS
->OpenProtocol (
812 &gEfiPciIoProtocolGuid
,
813 (VOID
**)&Dev
->PciIo
,
814 This
->DriverBindingHandle
,
816 EFI_OPEN_PROTOCOL_BY_DRIVER
818 if (EFI_ERROR (Status
)) {
822 Status
= PvScsiInit (Dev
);
823 if (EFI_ERROR (Status
)) {
828 // Setup complete, attempt to export the driver instance's PassThru interface
830 Dev
->Signature
= PVSCSI_SIG
;
831 Status
= gBS
->InstallProtocolInterface (
833 &gEfiExtScsiPassThruProtocolGuid
,
834 EFI_NATIVE_INTERFACE
,
837 if (EFI_ERROR (Status
)) {
849 &gEfiPciIoProtocolGuid
,
850 This
->DriverBindingHandle
,
863 PvScsiDriverBindingStop (
864 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
865 IN EFI_HANDLE ControllerHandle
,
866 IN UINTN NumberOfChildren
,
867 IN EFI_HANDLE
*ChildHandleBuffer
871 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
874 Status
= gBS
->OpenProtocol (
876 &gEfiExtScsiPassThruProtocolGuid
,
878 This
->DriverBindingHandle
,
880 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
882 if (EFI_ERROR (Status
)) {
886 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
888 Status
= gBS
->UninstallProtocolInterface (
890 &gEfiExtScsiPassThruProtocolGuid
,
893 if (EFI_ERROR (Status
)) {
901 &gEfiPciIoProtocolGuid
,
902 This
->DriverBindingHandle
,
911 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
912 &PvScsiDriverBindingSupported
,
913 &PvScsiDriverBindingStart
,
914 &PvScsiDriverBindingStop
,
915 PVSCSI_BINDING_VERSION
,
916 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
917 NULL
// DriverBindingHandle, filled as well
924 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
925 { "eng;en", L
"PVSCSI Host Driver" },
929 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
934 PvScsiGetDriverName (
935 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
937 OUT CHAR16
**DriverName
940 return LookupUnicodeString2 (
942 This
->SupportedLanguages
,
945 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
952 PvScsiGetDeviceName (
953 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
954 IN EFI_HANDLE DeviceHandle
,
955 IN EFI_HANDLE ChildHandle
,
957 OUT CHAR16
**ControllerName
960 return EFI_UNSUPPORTED
;
963 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
964 &PvScsiGetDriverName
,
965 &PvScsiGetDeviceName
,
966 "eng" // SupportedLanguages, ISO 639-2 language codes
969 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
970 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
971 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
972 "en" // SupportedLanguages, RFC 4646 language codes
982 IN EFI_HANDLE ImageHandle
,
983 IN EFI_SYSTEM_TABLE
*SystemTable
986 return EfiLibInstallDriverBindingComponentName2 (
989 &mPvScsiDriverBinding
,