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 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
35 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
45 for (Idx
= 0; Idx
< TARGET_MAX_BYTES
; ++Idx
) {
46 if (Target
[Idx
] != 0xFF) {
61 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
64 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
65 IN EFI_EVENT Event OPTIONAL
68 return EFI_UNSUPPORTED
;
74 PvScsiGetNextTargetLun (
75 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
76 IN OUT UINT8
**Target
,
85 return EFI_INVALID_PARAMETER
;
89 // The Target input parameter is unnecessarily a pointer-to-pointer
94 // If target not initialized, return first target & LUN
96 if (!IsTargetInitialized (TargetPtr
)) {
97 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
103 // We only use first byte of target identifer
105 LastTarget
= *TargetPtr
;
108 // Increment (target, LUN) pair if valid on input
110 Dev
= PVSCSI_FROM_PASS_THRU (This
);
111 if (LastTarget
> Dev
->MaxTarget
|| *Lun
> Dev
->MaxLun
) {
112 return EFI_INVALID_PARAMETER
;
115 if (*Lun
< Dev
->MaxLun
) {
120 if (LastTarget
< Dev
->MaxTarget
) {
123 *TargetPtr
= LastTarget
;
127 return EFI_NOT_FOUND
;
133 PvScsiBuildDevicePath (
134 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
137 IN OUT EFI_DEVICE_PATH_PROTOCOL
**DevicePath
142 SCSI_DEVICE_PATH
*ScsiDevicePath
;
144 if (DevicePath
== NULL
) {
145 return EFI_INVALID_PARAMETER
;
149 // We only use first byte of target identifer
151 TargetValue
= *Target
;
153 Dev
= PVSCSI_FROM_PASS_THRU (This
);
154 if (TargetValue
> Dev
->MaxTarget
|| Lun
> Dev
->MaxLun
) {
155 return EFI_NOT_FOUND
;
158 ScsiDevicePath
= AllocatePool (sizeof (*ScsiDevicePath
));
159 if (ScsiDevicePath
== NULL
) {
160 return EFI_OUT_OF_RESOURCES
;
163 ScsiDevicePath
->Header
.Type
= MESSAGING_DEVICE_PATH
;
164 ScsiDevicePath
->Header
.SubType
= MSG_SCSI_DP
;
165 ScsiDevicePath
->Header
.Length
[0] = (UINT8
)sizeof (*ScsiDevicePath
);
166 ScsiDevicePath
->Header
.Length
[1] = (UINT8
)(sizeof (*ScsiDevicePath
) >> 8);
167 ScsiDevicePath
->Pun
= TargetValue
;
168 ScsiDevicePath
->Lun
= (UINT16
)Lun
;
170 *DevicePath
= &ScsiDevicePath
->Header
;
178 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
179 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
184 SCSI_DEVICE_PATH
*ScsiDevicePath
;
187 if (DevicePath
== NULL
|| Target
== NULL
|| *Target
== NULL
|| Lun
== NULL
) {
188 return EFI_INVALID_PARAMETER
;
191 if (DevicePath
->Type
!= MESSAGING_DEVICE_PATH
||
192 DevicePath
->SubType
!= MSG_SCSI_DP
) {
193 return EFI_UNSUPPORTED
;
196 ScsiDevicePath
= (SCSI_DEVICE_PATH
*)DevicePath
;
197 Dev
= PVSCSI_FROM_PASS_THRU (This
);
198 if (ScsiDevicePath
->Pun
> Dev
->MaxTarget
||
199 ScsiDevicePath
->Lun
> Dev
->MaxLun
) {
200 return EFI_NOT_FOUND
;
204 // We only use first byte of target identifer
206 **Target
= (UINT8
)ScsiDevicePath
->Pun
;
207 ZeroMem (*Target
+ 1, TARGET_MAX_BYTES
- 1);
208 *Lun
= ScsiDevicePath
->Lun
;
217 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
220 return EFI_UNSUPPORTED
;
226 PvScsiResetTargetLun (
227 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
232 return EFI_UNSUPPORTED
;
238 PvScsiGetNextTarget (
239 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*This
,
240 IN OUT UINT8
**Target
247 if (Target
== NULL
) {
248 return EFI_INVALID_PARAMETER
;
252 // The Target input parameter is unnecessarily a pointer-to-pointer
257 // If target not initialized, return first target
259 if (!IsTargetInitialized (TargetPtr
)) {
260 ZeroMem (TargetPtr
, TARGET_MAX_BYTES
);
265 // We only use first byte of target identifer
267 LastTarget
= *TargetPtr
;
270 // Increment target if valid on input
272 Dev
= PVSCSI_FROM_PASS_THRU (This
);
273 if (LastTarget
> Dev
->MaxTarget
) {
274 return EFI_INVALID_PARAMETER
;
277 if (LastTarget
< Dev
->MaxTarget
) {
279 *TargetPtr
= LastTarget
;
283 return EFI_NOT_FOUND
;
288 PvScsiSetPciAttributes (
289 IN OUT PVSCSI_DEV
*Dev
295 // Backup original PCI Attributes
297 Status
= Dev
->PciIo
->Attributes (
299 EfiPciIoAttributeOperationGet
,
301 &Dev
->OriginalPciAttributes
303 if (EFI_ERROR (Status
)) {
308 // TODO: Change PCI Attributes
316 PvScsiRestorePciAttributes (
320 Dev
->PciIo
->Attributes (
322 EfiPciIoAttributeOperationSet
,
323 Dev
->OriginalPciAttributes
,
331 IN OUT PVSCSI_DEV
*Dev
337 // Init configuration
339 Dev
->MaxTarget
= PcdGet8 (PcdPvScsiMaxTargetLimit
);
340 Dev
->MaxLun
= PcdGet8 (PcdPvScsiMaxLunLimit
);
343 // Set PCI Attributes
345 Status
= PvScsiSetPciAttributes (Dev
);
346 if (EFI_ERROR (Status
)) {
351 // Populate the exported interface's attributes
353 Dev
->PassThru
.Mode
= &Dev
->PassThruMode
;
354 Dev
->PassThru
.PassThru
= &PvScsiPassThru
;
355 Dev
->PassThru
.GetNextTargetLun
= &PvScsiGetNextTargetLun
;
356 Dev
->PassThru
.BuildDevicePath
= &PvScsiBuildDevicePath
;
357 Dev
->PassThru
.GetTargetLun
= &PvScsiGetTargetLun
;
358 Dev
->PassThru
.ResetChannel
= &PvScsiResetChannel
;
359 Dev
->PassThru
.ResetTargetLun
= &PvScsiResetTargetLun
;
360 Dev
->PassThru
.GetNextTarget
= &PvScsiGetNextTarget
;
363 // AdapterId is a target for which no handle will be created during bus scan.
364 // Prevent any conflict with real devices.
366 Dev
->PassThruMode
.AdapterId
= MAX_UINT32
;
369 // Set both physical and logical attributes for non-RAID SCSI channel
371 Dev
->PassThruMode
.Attributes
= EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL
|
372 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL
;
375 // No restriction on transfer buffer alignment
377 Dev
->PassThruMode
.IoAlign
= 0;
385 IN OUT PVSCSI_DEV
*Dev
388 PvScsiRestorePciAttributes (Dev
);
398 PvScsiDriverBindingSupported (
399 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
400 IN EFI_HANDLE ControllerHandle
,
401 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
405 EFI_PCI_IO_PROTOCOL
*PciIo
;
408 Status
= gBS
->OpenProtocol (
410 &gEfiPciIoProtocolGuid
,
412 This
->DriverBindingHandle
,
414 EFI_OPEN_PROTOCOL_BY_DRIVER
416 if (EFI_ERROR (Status
)) {
420 Status
= PciIo
->Pci
.Read (
424 sizeof (Pci
) / sizeof (UINT32
),
427 if (EFI_ERROR (Status
)) {
431 if ((Pci
.Hdr
.VendorId
!= PCI_VENDOR_ID_VMWARE
) ||
432 (Pci
.Hdr
.DeviceId
!= PCI_DEVICE_ID_VMWARE_PVSCSI
)) {
433 Status
= EFI_UNSUPPORTED
;
437 Status
= EFI_SUCCESS
;
442 &gEfiPciIoProtocolGuid
,
443 This
->DriverBindingHandle
,
453 PvScsiDriverBindingStart (
454 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
455 IN EFI_HANDLE ControllerHandle
,
456 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath OPTIONAL
462 Dev
= (PVSCSI_DEV
*) AllocateZeroPool (sizeof (*Dev
));
464 return EFI_OUT_OF_RESOURCES
;
467 Status
= gBS
->OpenProtocol (
469 &gEfiPciIoProtocolGuid
,
470 (VOID
**)&Dev
->PciIo
,
471 This
->DriverBindingHandle
,
473 EFI_OPEN_PROTOCOL_BY_DRIVER
475 if (EFI_ERROR (Status
)) {
479 Status
= PvScsiInit (Dev
);
480 if (EFI_ERROR (Status
)) {
485 // Setup complete, attempt to export the driver instance's PassThru interface
487 Dev
->Signature
= PVSCSI_SIG
;
488 Status
= gBS
->InstallProtocolInterface (
490 &gEfiExtScsiPassThruProtocolGuid
,
491 EFI_NATIVE_INTERFACE
,
494 if (EFI_ERROR (Status
)) {
506 &gEfiPciIoProtocolGuid
,
507 This
->DriverBindingHandle
,
520 PvScsiDriverBindingStop (
521 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
522 IN EFI_HANDLE ControllerHandle
,
523 IN UINTN NumberOfChildren
,
524 IN EFI_HANDLE
*ChildHandleBuffer
528 EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
;
531 Status
= gBS
->OpenProtocol (
533 &gEfiExtScsiPassThruProtocolGuid
,
535 This
->DriverBindingHandle
,
537 EFI_OPEN_PROTOCOL_GET_PROTOCOL
// Lookup only
539 if (EFI_ERROR (Status
)) {
543 Dev
= PVSCSI_FROM_PASS_THRU (PassThru
);
545 Status
= gBS
->UninstallProtocolInterface (
547 &gEfiExtScsiPassThruProtocolGuid
,
550 if (EFI_ERROR (Status
)) {
558 &gEfiPciIoProtocolGuid
,
559 This
->DriverBindingHandle
,
568 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding
= {
569 &PvScsiDriverBindingSupported
,
570 &PvScsiDriverBindingStart
,
571 &PvScsiDriverBindingStop
,
572 PVSCSI_BINDING_VERSION
,
573 NULL
, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
574 NULL
// DriverBindingHandle, filled as well
581 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable
[] = {
582 { "eng;en", L
"PVSCSI Host Driver" },
586 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
;
591 PvScsiGetDriverName (
592 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
594 OUT CHAR16
**DriverName
597 return LookupUnicodeString2 (
599 This
->SupportedLanguages
,
602 (BOOLEAN
)(This
== &mComponentName
) // Iso639Language
609 PvScsiGetDeviceName (
610 IN EFI_COMPONENT_NAME_PROTOCOL
*This
,
611 IN EFI_HANDLE DeviceHandle
,
612 IN EFI_HANDLE ChildHandle
,
614 OUT CHAR16
**ControllerName
617 return EFI_UNSUPPORTED
;
620 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName
= {
621 &PvScsiGetDriverName
,
622 &PvScsiGetDeviceName
,
623 "eng" // SupportedLanguages, ISO 639-2 language codes
626 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2
= {
627 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME
) &PvScsiGetDriverName
,
628 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME
) &PvScsiGetDeviceName
,
629 "en" // SupportedLanguages, RFC 4646 language codes
639 IN EFI_HANDLE ImageHandle
,
640 IN EFI_SYSTEM_TABLE
*SystemTable
643 return EfiLibInstallDriverBindingComponentName2 (
646 &mPvScsiDriverBinding
,