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/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiLib.h>
18 #include <Protocol/PciIo.h>
19 #include <Uefi/UefiSpec.h>
24 // Higher versions will be used before lower, 0x10-0xffffffef is the version
25 // range for IHV (Indie Hardware Vendors)
27 #define PVSCSI_BINDING_VERSION 0x10
30 // Ext SCSI Pass Thru utilities
34 Writes a 32-bit value into BAR0 using MMIO
39 IN CONST PVSCSI_DEV
*Dev
,
44 return Dev
->PciIo
->Mem
.Write (
55 Writes multiple words of data into BAR0 using MMIO
59 PvScsiMmioWrite32Multiple (
60 IN CONST PVSCSI_DEV
*Dev
,
66 return Dev
->PciIo
->Mem
.Write (
68 EfiPciIoWidthFifoUint32
,
77 Send a PVSCSI command to device.
79 @param[in] Dev The pvscsi host device.
80 @param[in] Cmd The command to send to device.
81 @param[in] OPTIONAL DescWords An optional command descriptor (If command
82 have a descriptor). The descriptor is
83 provided as an array of UINT32 words and
84 is must be 32-bit aligned.
85 @param[in] DescWordsCount The number of words in command descriptor.
86 Caller must specify here 0 if DescWords
87 is not supplied (It is optional). In that
88 case, DescWords is ignored.
90 @return Status codes returned by Dev->PciIo->Mem.Write().
96 IN CONST PVSCSI_DEV
*Dev
,
98 IN UINT32
*DescWords OPTIONAL
,
99 IN UINTN DescWordsCount
104 if (DescWordsCount
> PVSCSI_MAX_CMD_DATA_WORDS
) {
105 return EFI_INVALID_PARAMETER
;
108 Status
= PvScsiMmioWrite32 (Dev
, PvScsiRegOffsetCommand
, Cmd
);
109 if (EFI_ERROR (Status
)) {
113 if (DescWordsCount
> 0) {
114 return PvScsiMmioWrite32Multiple (
116 PvScsiRegOffsetCommandData
,
128 IN CONST PVSCSI_DEV
*Dev
131 return PvScsiWriteCmdDesc (Dev
, PvScsiCmdAdapterReset
, NULL
, 0);
135 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
136 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
140 IsTargetInitialized (
146 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
147 if (Target
[Idx
] != 0xFF) {
155 // Ext SCSI Pass Thru
162 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
165 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
166 IN EFI_EVENT Event OPTIONAL
169 return EFI_UNSUPPORTED
;
175 PvScsiGetNextTargetLun (
176 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
177 IN OUT UINT8
**Target
,
185 if (Target
== NULL
) {
186 return EFI_INVALID_PARAMETER
;
190 // The Target input parameter is unnecessarily a pointer-to-pointer
195 // If target not initialized, return first target & LUN
197 if (!IsTargetInitialized (TargetPtr
)) {
198 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
204 // We only use first byte of target identifer
206 LastTarget
= *TargetPtr
;
209 // Increment (target, LUN) pair if valid on input
211 Dev
= PVSCSI_FROM_PASS_THRU (This
);
212 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
213 return EFI_INVALID_PARAMETER
;
216 if (*Lun
< Dev
->MaxLun
) {
221 if (LastTarget
< Dev
->MaxTarget
) {
224 *TargetPtr
= LastTarget
;
228 return EFI_NOT_FOUND
;
234 PvScsiBuildDevicePath (
235 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
238 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
243 SCSI_DEVICE_PATH
*ScsiDevicePath
;
245 if (DevicePath
== NULL
) {
246 return EFI_INVALID_PARAMETER
;
250 // We only use first byte of target identifer
252 TargetValue
= *Target
;
254 Dev
= PVSCSI_FROM_PASS_THRU (This
);
255 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
) {
256 return EFI_NOT_FOUND
;
259 ScsiDevicePath
= AllocatePool (sizeof (*ScsiDevicePath
));
260 if (ScsiDevicePath
== NULL
) {
261 return EFI_OUT_OF_RESOURCES
;
264 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
265 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
266 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
267 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
268 ScsiDevicePath
->Pun
= TargetValue
;
269 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
271 *DevicePath
= &ScsiDevicePath
->Header
;
279 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
280 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
285 SCSI_DEVICE_PATH
*ScsiDevicePath
;
288 if (DevicePath
== NULL
|| Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
289 return EFI_INVALID_PARAMETER
;
292 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
293 DevicePath
->SubType
!= MSG_SCSI_DP
) {
294 return EFI_UNSUPPORTED
;
297 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
298 Dev
= PVSCSI_FROM_PASS_THRU (This
);
299 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
300 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
301 return EFI_NOT_FOUND
;
305 // We only use first byte of target identifer
307 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
308 ZeroMem (*Target
+ 1, TARGET_MAX_BYTES
- 1);
309 *Lun
= ScsiDevicePath
->Lun
;
318 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
321 return EFI_UNSUPPORTED
;
327 PvScsiResetTargetLun (
328 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
333 return EFI_UNSUPPORTED
;
339 PvScsiGetNextTarget (
340 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
341 IN OUT UINT8
**Target
348 if (Target
== NULL
) {
349 return EFI_INVALID_PARAMETER
;
353 // The Target input parameter is unnecessarily a pointer-to-pointer
358 // If target not initialized, return first target
360 if (!IsTargetInitialized (TargetPtr
)) {
361 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
366 // We only use first byte of target identifer
368 LastTarget
= *TargetPtr
;
371 // Increment target if valid on input
373 Dev
= PVSCSI_FROM_PASS_THRU (This
);
374 if (LastTarget
> Dev
->MaxTarget
) {
375 return EFI_INVALID_PARAMETER
;
378 if (LastTarget
< Dev
->MaxTarget
) {
380 *TargetPtr
= LastTarget
;
384 return EFI_NOT_FOUND
;
389 PvScsiSetPciAttributes (
390 IN OUT PVSCSI_DEV
*Dev
396 // Backup original PCI Attributes
398 Status
= Dev
->PciIo
->Attributes (
400 EfiPciIoAttributeOperationGet
,
402 &Dev
->OriginalPciAttributes
404 if (EFI_ERROR (Status
)) {
409 // Enable MMIO-Space & Bus-Mastering
411 Status
= Dev
->PciIo
->Attributes (
413 EfiPciIoAttributeOperationEnable
,
414 (EFI_PCI_IO_ATTRIBUTE_MEMORY
|
415 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER
),
418 if (EFI_ERROR (Status
)) {
427 PvScsiRestorePciAttributes (
431 Dev
->PciIo
->Attributes (
433 EfiPciIoAttributeOperationSet
,
434 Dev
->OriginalPciAttributes
,
442 IN OUT PVSCSI_DEV
*Dev
448 // Init configuration
450 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
451 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
454 // Set PCI Attributes
456 Status
= PvScsiSetPciAttributes (Dev
);
457 if (EFI_ERROR (Status
)) {
464 Status
= PvScsiResetAdapter (Dev
);
465 if (EFI_ERROR (Status
)) {
466 goto RestorePciAttributes
;
470 // Populate the exported interface's attributes
472 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
473 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
474 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
475 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
476 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
477 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
478 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
479 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
482 // AdapterId is a target for which no handle will be created during bus scan.
483 // Prevent any conflict with real devices.
485 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
488 // Set both physical and logical attributes for non-RAID SCSI channel
490 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
491 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
494 // No restriction on transfer buffer alignment
496 Dev
->PassThruMode
.IoAlign
= 0;
500 RestorePciAttributes
:
501 PvScsiRestorePciAttributes (Dev
);
509 IN OUT PVSCSI_DEV
*Dev
512 PvScsiRestorePciAttributes (Dev
);
522 PvScsiDriverBindingSupported (
523 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
524 IN EFI_HANDLE ControllerHandle
,
525 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
529 EFI_PCI_IO_PROTOCOL
*PciIo
;
532 Status
= gBS
->OpenProtocol (
534 &gEfiPciIoProtocolGuid
,
536 This
->DriverBindingHandle
,
538 EFI_OPEN_PROTOCOL_BY_DRIVER
540 if (EFI_ERROR (Status
)) {
544 Status
= PciIo
->Pci
.Read (
548 sizeof (Pci
) / sizeof (UINT32
),
551 if (EFI_ERROR (Status
)) {
555 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
556 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
557 Status
= EFI_UNSUPPORTED
;
561 Status
= EFI_SUCCESS
;
566 &gEfiPciIoProtocolGuid
,
567 This
->DriverBindingHandle
,
577 PvScsiDriverBindingStart (
578 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
579 IN EFI_HANDLE ControllerHandle
,
580 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
586 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
588 return EFI_OUT_OF_RESOURCES
;
591 Status
= gBS
->OpenProtocol (
593 &gEfiPciIoProtocolGuid
,
594 (VOID
**)&Dev
->PciIo
,
595 This
->DriverBindingHandle
,
597 EFI_OPEN_PROTOCOL_BY_DRIVER
599 if (EFI_ERROR (Status
)) {
603 Status
= PvScsiInit (Dev
);
604 if (EFI_ERROR (Status
)) {
609 // Setup complete, attempt to export the driver instance's PassThru interface
611 Dev
->Signature
= PVSCSI_SIG
;
612 Status
= gBS
->InstallProtocolInterface (
614 &gEfiExtScsiPassThruProtocolGuid
,
615 EFI_NATIVE_INTERFACE
,
618 if (EFI_ERROR (Status
)) {
630 &gEfiPciIoProtocolGuid
,
631 This
->DriverBindingHandle
,
644 PvScsiDriverBindingStop (
645 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
646 IN EFI_HANDLE ControllerHandle
,
647 IN UINTN NumberOfChildren
,
648 IN EFI_HANDLE
*ChildHandleBuffer
652 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
655 Status
= gBS
->OpenProtocol (
657 &gEfiExtScsiPassThruProtocolGuid
,
659 This
->DriverBindingHandle
,
661 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
663 if (EFI_ERROR (Status
)) {
667 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
669 Status
= gBS
->UninstallProtocolInterface (
671 &gEfiExtScsiPassThruProtocolGuid
,
674 if (EFI_ERROR (Status
)) {
682 &gEfiPciIoProtocolGuid
,
683 This
->DriverBindingHandle
,
692 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
693 &PvScsiDriverBindingSupported
,
694 &PvScsiDriverBindingStart
,
695 &PvScsiDriverBindingStop
,
696 PVSCSI_BINDING_VERSION
,
697 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
698 NULL
// DriverBindingHandle, filled as well
705 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
706 { "eng;en", L
"PVSCSI Host Driver" },
710 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
715 PvScsiGetDriverName (
716 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
718 OUT CHAR16
**DriverName
721 return LookupUnicodeString2 (
723 This
->SupportedLanguages
,
726 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
733 PvScsiGetDeviceName (
734 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
735 IN EFI_HANDLE DeviceHandle
,
736 IN EFI_HANDLE ChildHandle
,
738 OUT CHAR16
**ControllerName
741 return EFI_UNSUPPORTED
;
744 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
745 &PvScsiGetDriverName
,
746 &PvScsiGetDeviceName
,
747 "eng" // SupportedLanguages, ISO 639-2 language codes
750 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
751 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
752 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
753 "en" // SupportedLanguages, RFC 4646 language codes
763 IN EFI_HANDLE ImageHandle
,
764 IN EFI_SYSTEM_TABLE
*SystemTable
767 return EfiLibInstallDriverBindingComponentName2 (
770 &mPvScsiDriverBinding
,