From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Bus/Scsi/ScsiBusDxe/ComponentName.c | 171 + .../MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.c | 1520 +++++ .../MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.h | 486 ++ .../MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.uni | 17 + .../Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf | 64 + .../Bus/Scsi/ScsiBusDxe/ScsiBusExtra.uni | 14 + .../Bus/Scsi/ScsiDiskDxe/ComponentName.c | 218 + .../MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c | 6327 ++++++++++++++++++++ .../MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.h | 1600 +++++ .../MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.uni | 16 + .../Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf | 71 + .../Bus/Scsi/ScsiDiskDxe/ScsiDiskExtra.uni | 14 + 12 files changed, 10518 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskExtra.uni (limited to 'roms/edk2/MdeModulePkg/Bus/Scsi') diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ComponentName.c new file mode 100644 index 000000000..e56eefee3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ComponentName.c @@ -0,0 +1,171 @@ +/** @file + UEFI Component Name(2) protocol implementation for SCSI bus driver. + +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "ScsiBus.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gScsiBusComponentName = { + ScsiBusComponentNameGetDriverName, + ScsiBusComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gScsiBusComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ScsiBusComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ScsiBusComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mScsiBusDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"SCSI Bus Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mScsiBusDriverNameTable, + DriverName, + (BOOLEAN)(This == &gScsiBusComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.c b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.c new file mode 100644 index 000000000..27b554ad3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.c @@ -0,0 +1,1520 @@ +/** @file + SCSI Bus driver that layers on every SCSI Pass Thru and + Extended SCSI Pass Thru protocol in the system. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "ScsiBus.h" + + +EFI_DRIVER_BINDING_PROTOCOL gSCSIBusDriverBinding = { + SCSIBusDriverBindingSupported, + SCSIBusDriverBindingStart, + SCSIBusDriverBindingStop, + 0xa, + NULL, + NULL +}; + +VOID *mWorkingBuffer; + +/** + Convert EFI_SCSI_IO_SCSI_REQUEST_PACKET packet to EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet. + + @param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET + @param CommandPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +**/ +EFI_STATUS +EFIAPI +ScsiioToPassThruPacket ( + IN EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, + OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *CommandPacket + ); + +/** + Convert EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet to EFI_SCSI_IO_SCSI_REQUEST_PACKET packet. + + @param ScsiPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + @param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET + +**/ +EFI_STATUS +EFIAPI +PassThruToScsiioPacket ( + IN EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket, + OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet + ); + +/** + Notify Function in which convert EFI1.0 PassThru Packet back to UEF2.0 + SCSI IO Packet. + + @param Event The instance of EFI_EVENT. + @param Context The parameter passed in. + +**/ +VOID +EFIAPI +NotifyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Allocates an aligned buffer for SCSI device. + + This function allocates an aligned buffer for the SCSI device to perform + SCSI pass through operations. The alignment requirement is from SCSI pass + through interface. + + @param ScsiIoDevice The SCSI child device involved for the operation. + @param BufferSize The request buffer size. + + @return A pointer to the aligned buffer or NULL if the allocation fails. + +**/ +VOID * +AllocateAlignedBuffer ( + IN SCSI_IO_DEV *ScsiIoDevice, + IN UINTN BufferSize + ) +{ + return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiIoDevice->ScsiIo.IoAlign); +} + +/** + Frees an aligned buffer for SCSI device. + + This function frees an aligned buffer for the SCSI device to perform + SCSI pass through operations. + + @param Buffer The aligned buffer to be freed. + @param BufferSize The request buffer size. + +**/ +VOID +FreeAlignedBuffer ( + IN VOID *Buffer, + IN UINTN BufferSize + ) +{ + if (Buffer != NULL) { + FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize)); + } +} + +/** + The user Entry Point for module ScsiBus. The user code starts with this function. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeScsiBus( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gSCSIBusDriverBinding, + ImageHandle, + &gScsiBusComponentName, + &gScsiBusComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + + +/** + Test to see if this driver supports ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Supported() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SCSI_PASS_THRU_PROTOCOL *PassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtPassThru; + UINT64 Lun; + UINT8 *TargetId; + SCSI_TARGET_ID ScsiTargetId; + + TargetId = &ScsiTargetId.ScsiId.ExtScsi[0]; + SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); + + // + // To keep backward compatibility, UEFI ExtPassThru Protocol is supported as well as + // EFI PassThru Protocol. From priority perspective, ExtPassThru Protocol is firstly + // tried to open on host controller handle. If fails, then PassThru Protocol is tried instead. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **)&ExtPassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } else if (!EFI_ERROR(Status)) { + // + // Check if RemainingDevicePath is NULL or the End of Device Path Node, + // if yes, return EFI_SUCCESS. + // + if ((RemainingDevicePath == NULL) || IsDevicePathEnd (RemainingDevicePath)) { + // + // Close protocol regardless of RemainingDevicePath validation + // + gBS->CloseProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_SUCCESS; + } else { + // + // If RemainingDevicePath isn't the End of Device Path Node, check its validation + // + Status = ExtPassThru->GetTargetLun (ExtPassThru, RemainingDevicePath, &TargetId, &Lun); + // + // Close protocol regardless of RemainingDevicePath validation + // + gBS->CloseProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (!EFI_ERROR(Status)) { + return EFI_SUCCESS; + } + } + } + + // + // Come here in 2 condition: + // 1. ExtPassThru doesn't exist. + // 2. ExtPassThru exists but RemainingDevicePath is invalid. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + (VOID **)&PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Test RemainingDevicePath is valid or not. + // + if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { + Status = PassThru->GetTargetLun (PassThru, RemainingDevicePath, &ScsiTargetId.ScsiId.Scsi, &Lun); + } + + gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; +} + + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Start() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + UINT64 Lun; + UINT8 *TargetId; + BOOLEAN ScanOtherPuns; + BOOLEAN FromFirstTarget; + BOOLEAN ExtScsiSupport; + EFI_STATUS Status; + EFI_STATUS DevicePathStatus; + EFI_STATUS PassThruStatus; + SCSI_BUS_DEVICE *ScsiBusDev; + SCSI_TARGET_ID ScsiTargetId; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiInterface; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiInterface; + EFI_SCSI_BUS_PROTOCOL *BusIdentify; + + TargetId = NULL; + ScanOtherPuns = TRUE; + FromFirstTarget = FALSE; + ExtScsiSupport = FALSE; + PassThruStatus = EFI_SUCCESS; + + TargetId = &ScsiTargetId.ScsiId.ExtScsi[0]; + SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); + + DevicePathStatus = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (DevicePathStatus) && (DevicePathStatus != EFI_ALREADY_STARTED)) { + return DevicePathStatus; + } + + // + // Report Status Code to indicate SCSI bus starts + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_SCSI | EFI_IOB_PC_INIT), + ParentDevicePath + ); + + // + // To keep backward compatibility, UEFI ExtPassThru Protocol is supported as well as + // EFI PassThru Protocol. From priority perspective, ExtPassThru Protocol is firstly + // tried to open on host controller handle. If fails, then PassThru Protocol is tried instead. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &ExtScsiInterface, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + // + // Fail to open UEFI ExtendPassThru Protocol, then try to open EFI PassThru Protocol instead. + // + if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + (VOID **) &ScsiInterface, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + // + // Fail to open EFI PassThru Protocol, Close the DevicePathProtocol if it is opened by this time. + // + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + if (!EFI_ERROR(DevicePathStatus)) { + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + return Status; + } + } else { + // + // Succeed to open ExtPassThru Protocol, and meanwhile open PassThru Protocol + // with BY_DRIVER if it is also present on the handle. The intent is to prevent + // another SCSI Bus Driver to work on the same host handle. + // + ExtScsiSupport = TRUE; + PassThruStatus = gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + (VOID **) &ScsiInterface, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + + if (Status != EFI_ALREADY_STARTED) { + // + // Go through here means either ExtPassThru or PassThru Protocol is successfully opened + // on this handle for this time. Then construct Host controller private data. + // + ScsiBusDev = NULL; + ScsiBusDev = AllocateZeroPool(sizeof(SCSI_BUS_DEVICE)); + if (ScsiBusDev == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + ScsiBusDev->Signature = SCSI_BUS_DEVICE_SIGNATURE; + ScsiBusDev->ExtScsiSupport = ExtScsiSupport; + ScsiBusDev->DevicePath = ParentDevicePath; + if (ScsiBusDev->ExtScsiSupport) { + ScsiBusDev->ExtScsiInterface = ExtScsiInterface; + } else { + ScsiBusDev->ScsiInterface = ScsiInterface; + } + + // + // Install EFI_SCSI_BUS_PROTOCOL to the controller handle, So ScsiBusDev could be + // retrieved on this controller handle. With ScsiBusDev, we can know which PassThru + // Protocol is present on the handle, UEFI ExtPassThru Protocol or EFI PassThru Protocol. + // + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + &ScsiBusDev->BusIdentify + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + } else { + // + // Go through here means Start() is re-invoked again, nothing special is required to do except + // picking up Host controller private information. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &BusIdentify, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + ScsiBusDev = SCSI_BUS_CONTROLLER_DEVICE_FROM_THIS (BusIdentify); + } + + // + // Report Status Code to indicate detecting devices on bus + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_SCSI | EFI_IOB_PC_DETECT), + ParentDevicePath + ); + + Lun = 0; + if (RemainingDevicePath == NULL) { + // + // If RemainingDevicePath is NULL, + // must enumerate all SCSI devices anyway + // + FromFirstTarget = TRUE; + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // only scan the specified device by RemainingDevicePath + // + if (ScsiBusDev->ExtScsiSupport) { + Status = ScsiBusDev->ExtScsiInterface->GetTargetLun (ScsiBusDev->ExtScsiInterface, RemainingDevicePath, &TargetId, &Lun); + } else { + Status = ScsiBusDev->ScsiInterface->GetTargetLun (ScsiBusDev->ScsiInterface, RemainingDevicePath, &ScsiTargetId.ScsiId.Scsi, &Lun); + } + + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // If RemainingDevicePath is the End of Device Path Node, + // skip enumerate any device and return EFI_SUCCESS + // + ScanOtherPuns = FALSE; + } + + while(ScanOtherPuns) { + if (FromFirstTarget) { + // + // Remaining Device Path is NULL, scan all the possible Puns in the + // SCSI Channel. + // + if (ScsiBusDev->ExtScsiSupport) { + Status = ScsiBusDev->ExtScsiInterface->GetNextTargetLun (ScsiBusDev->ExtScsiInterface, &TargetId, &Lun); + } else { + Status = ScsiBusDev->ScsiInterface->GetNextDevice (ScsiBusDev->ScsiInterface, &ScsiTargetId.ScsiId.Scsi, &Lun); + } + if (EFI_ERROR (Status)) { + // + // no legal Pun and Lun found any more + // + break; + } + } else { + ScanOtherPuns = FALSE; + } + // + // Avoid creating handle for the host adapter. + // + if (ScsiBusDev->ExtScsiSupport) { + if ((ScsiTargetId.ScsiId.Scsi) == ScsiBusDev->ExtScsiInterface->Mode->AdapterId) { + continue; + } + } else { + if ((ScsiTargetId.ScsiId.Scsi) == ScsiBusDev->ScsiInterface->Mode->AdapterId) { + continue; + } + } + // + // Scan for the scsi device, if it attaches to the scsi bus, + // then create handle and install scsi i/o protocol. + // + Status = ScsiScanCreateDevice (This, Controller, &ScsiTargetId, Lun, ScsiBusDev); + } + return EFI_SUCCESS; + +ErrorExit: + + if (ScsiBusDev != NULL) { + FreePool (ScsiBusDev); + } + + if (ExtScsiSupport) { + gBS->CloseProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (!EFI_ERROR (PassThruStatus)) { + gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + } else { + gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + return Status; +} + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). + In order to make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() must follow these + calling restrictions. If any other agent wishes to call Stop() it must also + follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + EFI_SCSI_IO_PROTOCOL *ScsiIo; + SCSI_IO_DEV *ScsiIoDevice; + VOID *ScsiPassThru; + EFI_SCSI_BUS_PROTOCOL *Scsidentifier; + SCSI_BUS_DEVICE *ScsiBusDev; + + if (NumberOfChildren == 0) { + // + // Get the SCSI_BUS_DEVICE + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &Scsidentifier, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + ScsiBusDev = SCSI_BUS_CONTROLLER_DEVICE_FROM_THIS (Scsidentifier); + + // + // Uninstall SCSI Bus Protocol + // + gBS->UninstallProtocolInterface ( + Controller, + &gEfiCallerIdGuid, + &ScsiBusDev->BusIdentify + ); + + // + // Close the bus driver + // + if (ScsiBusDev->ExtScsiSupport) { + // + // Close ExtPassThru Protocol from this controller handle + // + gBS->CloseProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + // + // When Start() succeeds to open ExtPassThru, it always tries to open PassThru BY_DRIVER. + // Its intent is to prevent another SCSI Bus Driver from working on the same host handle. + // So Stop() needs to try to close PassThru if present here. + // + gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } else { + gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + FreePool (ScsiBusDev); + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiScsiIoProtocolGuid, + (VOID **) &ScsiIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + continue; + } + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (ScsiIo); + // + // Close the child handle + // + if (ScsiIoDevice->ExtScsiSupport) { + Status = gBS->CloseProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + } else { + Status = gBS->CloseProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + ScsiIoDevice->DevicePath, + &gEfiScsiIoProtocolGuid, + &ScsiIoDevice->ScsiIo, + NULL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + if (ScsiIoDevice->ExtScsiSupport) { + gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + &ScsiPassThru, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + &ScsiPassThru, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + } else { + FreePool (ScsiIoDevice); + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Retrieves the device type information of the SCSI Controller. + + @param This Protocol instance pointer. + @param DeviceType A pointer to the device type information retrieved from + the SCSI Controller. + + @retval EFI_SUCCESS Retrieves the device type information successfully. + @retval EFI_INVALID_PARAMETER The DeviceType is NULL. + +**/ +EFI_STATUS +EFIAPI +ScsiGetDeviceType ( + IN EFI_SCSI_IO_PROTOCOL *This, + OUT UINT8 *DeviceType + ) +{ + SCSI_IO_DEV *ScsiIoDevice; + + if (DeviceType == NULL) { + return EFI_INVALID_PARAMETER; + } + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); + *DeviceType = ScsiIoDevice->ScsiDeviceType; + return EFI_SUCCESS; +} + + +/** + Retrieves the device location in the SCSI channel. + + @param This Protocol instance pointer. + @param Target A pointer to the Target ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of the SCSI device on + the SCSI channel. + + @retval EFI_SUCCESS Retrieves the device location successfully. + @retval EFI_INVALID_PARAMETER The Target or Lun is NULL. + +**/ +EFI_STATUS +EFIAPI +ScsiGetDeviceLocation ( + IN EFI_SCSI_IO_PROTOCOL *This, + IN OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + SCSI_IO_DEV *ScsiIoDevice; + + if (Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); + + CopyMem (*Target,&ScsiIoDevice->Pun, TARGET_MAX_BYTES); + + *Lun = ScsiIoDevice->Lun; + + return EFI_SUCCESS; +} + +/** + Resets the SCSI Bus that the SCSI Controller is attached to. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The SCSI bus is reset successfully. + @retval EFI_DEVICE_ERROR Errors encountered when resetting the SCSI bus. + @retval EFI_UNSUPPORTED The bus reset operation is not supported by the + SCSI Host Controller. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset + the SCSI bus. +**/ +EFI_STATUS +EFIAPI +ScsiResetBus ( + IN EFI_SCSI_IO_PROTOCOL *This + ) +{ + SCSI_IO_DEV *ScsiIoDevice; + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); + + // + // Report Status Code to indicate reset happens + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), + ScsiIoDevice->ScsiBusDeviceData->DevicePath + ); + + if (ScsiIoDevice->ExtScsiSupport){ + return ScsiIoDevice->ExtScsiPassThru->ResetChannel (ScsiIoDevice->ExtScsiPassThru); + } else { + return ScsiIoDevice->ScsiPassThru->ResetChannel (ScsiIoDevice->ScsiPassThru); + } +} + + +/** + Resets the SCSI Controller that the device handle specifies. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Reset the SCSI controller successfully. + @retval EFI_DEVICE_ERROR Errors are encountered when resetting the SCSI Controller. + @retval EFI_UNSUPPORTED The SCSI bus does not support a device reset operation. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the + SCSI Controller. +**/ +EFI_STATUS +EFIAPI +ScsiResetDevice ( + IN EFI_SCSI_IO_PROTOCOL *This + ) +{ + SCSI_IO_DEV *ScsiIoDevice; + UINT8 Target[TARGET_MAX_BYTES]; + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); + + // + // Report Status Code to indicate reset happens + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), + ScsiIoDevice->ScsiBusDeviceData->DevicePath + ); + + CopyMem (Target,&ScsiIoDevice->Pun, TARGET_MAX_BYTES); + + + if (ScsiIoDevice->ExtScsiSupport) { + return ScsiIoDevice->ExtScsiPassThru->ResetTargetLun ( + ScsiIoDevice->ExtScsiPassThru, + Target, + ScsiIoDevice->Lun + ); + } else { + return ScsiIoDevice->ScsiPassThru->ResetTarget ( + ScsiIoDevice->ScsiPassThru, + ScsiIoDevice->Pun.ScsiId.Scsi, + ScsiIoDevice->Lun + ); + } +} + + +/** + Sends a SCSI Request Packet to the SCSI Controller for execution. + + @param This Protocol instance pointer. + @param CommandPacket The SCSI request packet to send to the SCSI + Controller specified by the device handle. + @param Event If the SCSI bus where the SCSI device is attached + does not support non-blocking I/O, then Event is + ignored, and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non-blocking I/O is + supported, then non-blocking I/O is performed, + and Event will be signaled when the SCSI Request + Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host + successfully, and TransferLength bytes were + transferred to/from DataBuffer.See + HostAdapterStatus, TargetStatus, + SenseDataLength, and SenseData in that order + for additional status information. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was executed, + but the entire DataBuffer could not be transferred. + The actual number of bytes transferred is returned + in TransferLength. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because + there are too many SCSI Command Packets already + queued.The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SCSI Request Packet. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. + @retval EFI_INVALID_PARAMETER The contents of CommandPacket are invalid. + The SCSI Request Packet was not sent, so no + additional status information is available. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet + is not supported by the SCSI initiator(i.e., SCSI + Host Controller). The SCSI Request Packet was not + sent, so no additional status information is + available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI + Request Packet to execute. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. +**/ +EFI_STATUS +EFIAPI +ScsiExecuteSCSICommand ( + IN EFI_SCSI_IO_PROTOCOL *This, + IN OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + SCSI_IO_DEV *ScsiIoDevice; + EFI_STATUS Status; + UINT8 Target[TARGET_MAX_BYTES]; + EFI_EVENT PacketEvent; + EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ExtRequestPacket; + SCSI_EVENT_DATA EventData; + + PacketEvent = NULL; + + if (Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); + CopyMem (Target,&ScsiIoDevice->Pun, TARGET_MAX_BYTES); + + if (ScsiIoDevice->ExtScsiSupport) { + ExtRequestPacket = (EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *) Packet; + + if (((ScsiIoDevice->ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO) != 0) && (Event != NULL)) { + Status = ScsiIoDevice->ExtScsiPassThru->PassThru ( + ScsiIoDevice->ExtScsiPassThru, + Target, + ScsiIoDevice->Lun, + ExtRequestPacket, + Event + ); + } else { + // + // If there's no event or the SCSI Device doesn't support NON-BLOCKING, + // let the 'Event' parameter for PassThru() be NULL. + // + Status = ScsiIoDevice->ExtScsiPassThru->PassThru ( + ScsiIoDevice->ExtScsiPassThru, + Target, + ScsiIoDevice->Lun, + ExtRequestPacket, + NULL + ); + if ((!EFI_ERROR(Status)) && (Event != NULL)) { + // + // Signal Event to tell caller to pick up the SCSI IO packet if the + // PassThru() succeeds. + // + gBS->SignalEvent (Event); + } + } + } else { + + mWorkingBuffer = AllocatePool (sizeof(EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); + + if (mWorkingBuffer == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // Convert package into EFI1.0, EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET. + // + Status = ScsiioToPassThruPacket(Packet, (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET*)mWorkingBuffer); + if (EFI_ERROR(Status)) { + FreePool(mWorkingBuffer); + return Status; + } + + if (((ScsiIoDevice->ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO) != 0) && (Event != NULL)) { + EventData.Data1 = (VOID*)Packet; + EventData.Data2 = Event; + // + // Create Event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + NotifyFunction, + &EventData, + &PacketEvent + ); + if (EFI_ERROR(Status)) { + FreePool(mWorkingBuffer); + return Status; + } + + Status = ScsiIoDevice->ScsiPassThru->PassThru ( + ScsiIoDevice->ScsiPassThru, + ScsiIoDevice->Pun.ScsiId.Scsi, + ScsiIoDevice->Lun, + mWorkingBuffer, + PacketEvent + ); + + if (EFI_ERROR(Status)) { + FreePool(mWorkingBuffer); + gBS->CloseEvent(PacketEvent); + return Status; + } + + } else { + // + // If there's no event or SCSI Device doesn't support NON-BLOCKING, just convert + // EFI1.0 PassThru packet back to UEFI2.0 SCSI IO Packet. + // + Status = ScsiIoDevice->ScsiPassThru->PassThru ( + ScsiIoDevice->ScsiPassThru, + ScsiIoDevice->Pun.ScsiId.Scsi, + ScsiIoDevice->Lun, + mWorkingBuffer, + NULL + ); + if (EFI_ERROR(Status)) { + FreePool(mWorkingBuffer); + return Status; + } + + PassThruToScsiioPacket((EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET*)mWorkingBuffer,Packet); + // + // After converting EFI1.0 PassThru Packet back to UEFI2.0 SCSI IO Packet, + // free mWorkingBuffer. + // + FreePool(mWorkingBuffer); + + // + // Signal Event to tell caller to pick up the SCSI IO Packet. + // + if (Event != NULL) { + gBS->SignalEvent (Event); + } + } + } + return Status; +} + + +/** + Scan SCSI Bus to discover the device, and attach ScsiIoProtocol to it. + + @param This Protocol instance pointer + @param Controller Controller handle + @param TargetId Target to be scanned + @param Lun The Lun of the SCSI device on the SCSI channel. + @param ScsiBusDev The pointer of SCSI_BUS_DEVICE + + @retval EFI_SUCCESS Successfully to discover the device and attach + ScsiIoProtocol to it. + @retval EFI_OUT_OF_RESOURCES Fail to discover the device. + +**/ +EFI_STATUS +EFIAPI +ScsiScanCreateDevice ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN SCSI_TARGET_ID *TargetId, + IN UINT64 Lun, + IN OUT SCSI_BUS_DEVICE *ScsiBusDev + ) +{ + EFI_STATUS Status; + SCSI_IO_DEV *ScsiIoDevice; + EFI_DEVICE_PATH_PROTOCOL *ScsiDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_HANDLE DeviceHandle; + + DevicePath = NULL; + RemainingDevicePath = NULL; + ScsiDevicePath = NULL; + ScsiIoDevice = NULL; + + // + // Build Device Path + // + if (ScsiBusDev->ExtScsiSupport){ + Status = ScsiBusDev->ExtScsiInterface->BuildDevicePath ( + ScsiBusDev->ExtScsiInterface, + &TargetId->ScsiId.ExtScsi[0], + Lun, + &ScsiDevicePath + ); + } else { + Status = ScsiBusDev->ScsiInterface->BuildDevicePath ( + ScsiBusDev->ScsiInterface, + TargetId->ScsiId.Scsi, + Lun, + &ScsiDevicePath + ); + } + + if (EFI_ERROR(Status)) { + return Status; + } + + DevicePath = AppendDevicePathNode ( + ScsiBusDev->DevicePath, + ScsiDevicePath + ); + + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + DeviceHandle = NULL; + RemainingDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle); + if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(RemainingDevicePath)) { + // + // The device has been started, directly return to fast boot. + // + Status = EFI_ALREADY_STARTED; + goto ErrorExit; + } + + ScsiIoDevice = AllocateZeroPool (sizeof (SCSI_IO_DEV)); + if (ScsiIoDevice == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + ScsiIoDevice->Signature = SCSI_IO_DEV_SIGNATURE; + ScsiIoDevice->ScsiBusDeviceData = ScsiBusDev; + CopyMem(&ScsiIoDevice->Pun, TargetId, TARGET_MAX_BYTES); + ScsiIoDevice->Lun = Lun; + + if (ScsiBusDev->ExtScsiSupport) { + ScsiIoDevice->ExtScsiPassThru = ScsiBusDev->ExtScsiInterface; + ScsiIoDevice->ExtScsiSupport = TRUE; + ScsiIoDevice->ScsiIo.IoAlign = ScsiIoDevice->ExtScsiPassThru->Mode->IoAlign; + + } else { + ScsiIoDevice->ScsiPassThru = ScsiBusDev->ScsiInterface; + ScsiIoDevice->ExtScsiSupport = FALSE; + ScsiIoDevice->ScsiIo.IoAlign = ScsiIoDevice->ScsiPassThru->Mode->IoAlign; + } + + ScsiIoDevice->ScsiIo.GetDeviceType = ScsiGetDeviceType; + ScsiIoDevice->ScsiIo.GetDeviceLocation = ScsiGetDeviceLocation; + ScsiIoDevice->ScsiIo.ResetBus = ScsiResetBus; + ScsiIoDevice->ScsiIo.ResetDevice = ScsiResetDevice; + ScsiIoDevice->ScsiIo.ExecuteScsiCommand = ScsiExecuteSCSICommand; + + // + // Report Status Code here since the new SCSI device will be discovered + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + (EFI_IO_BUS_SCSI | EFI_IOB_PC_ENABLE), + ScsiBusDev->DevicePath + ); + + if (!DiscoverScsiDevice (ScsiIoDevice)) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + ScsiIoDevice->DevicePath = DevicePath; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ScsiIoDevice->Handle, + &gEfiDevicePathProtocolGuid, + ScsiIoDevice->DevicePath, + &gEfiScsiIoProtocolGuid, + &ScsiIoDevice->ScsiIo, + NULL + ); + if (EFI_ERROR (Status)) { + goto ErrorExit; + } else { + if (ScsiBusDev->ExtScsiSupport) { + gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &(ScsiBusDev->ExtScsiInterface), + This->DriverBindingHandle, + ScsiIoDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + gBS->OpenProtocol ( + Controller, + &gEfiScsiPassThruProtocolGuid, + (VOID **) &(ScsiBusDev->ScsiInterface), + This->DriverBindingHandle, + ScsiIoDevice->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + } + return EFI_SUCCESS; + +ErrorExit: + + // + // The memory space for ScsiDevicePath is allocated in + // ScsiPassThru->BuildDevicePath() function; It is no longer used + // after AppendDevicePathNode,so free the memory it occupies. + // + FreePool (ScsiDevicePath); + + if (DevicePath != NULL) { + FreePool (DevicePath); + } + + if (ScsiIoDevice != NULL) { + FreePool (ScsiIoDevice); + } + + return Status; +} + + +/** + Discovery SCSI Device + + @param ScsiIoDevice The pointer of SCSI_IO_DEV + + @retval TRUE Find SCSI Device and verify it. + @retval FALSE Unable to find SCSI Device. + +**/ +BOOLEAN +DiscoverScsiDevice ( + IN OUT SCSI_IO_DEV *ScsiIoDevice + ) +{ + EFI_STATUS Status; + UINT32 InquiryDataLength; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + EFI_SCSI_INQUIRY_DATA *InquiryData; + EFI_SCSI_SENSE_DATA *SenseData; + UINT8 MaxRetry; + UINT8 Index; + BOOLEAN ScsiDeviceFound; + + HostAdapterStatus = 0; + TargetStatus = 0; + SenseData = NULL; + + InquiryData = AllocateAlignedBuffer (ScsiIoDevice, sizeof (EFI_SCSI_INQUIRY_DATA)); + if (InquiryData == NULL) { + ScsiDeviceFound = FALSE; + goto Done; + } + + SenseData = AllocateAlignedBuffer ( + ScsiIoDevice, + sizeof (EFI_SCSI_SENSE_DATA) + ); + if (SenseData == NULL) { + ScsiDeviceFound = FALSE; + goto Done; + } + + // + // Using Inquiry command to scan for the device + // + InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA); + SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA); + ZeroMem (InquiryData, InquiryDataLength); + ZeroMem (SenseData, SenseDataLength); + + MaxRetry = 2; + for (Index = 0; Index < MaxRetry; Index++) { + Status = ScsiInquiryCommand ( + &ScsiIoDevice->ScsiIo, + SCSI_BUS_TIMEOUT, + SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) InquiryData, + &InquiryDataLength, + FALSE + ); + if (!EFI_ERROR (Status)) { + if ((HostAdapterStatus == EFI_SCSI_IO_STATUS_HOST_ADAPTER_OK) && + (TargetStatus == EFI_SCSI_IO_STATUS_TARGET_CHECK_CONDITION) && + (SenseData->Error_Code == 0x70) && + (SenseData->Sense_Key == EFI_SCSI_SK_ILLEGAL_REQUEST)) { + ScsiDeviceFound = FALSE; + goto Done; + } + break; + } + if ((Status == EFI_BAD_BUFFER_SIZE) || + (Status == EFI_INVALID_PARAMETER) || + (Status == EFI_UNSUPPORTED)) { + ScsiDeviceFound = FALSE; + goto Done; + } + } + + if (Index == MaxRetry) { + ScsiDeviceFound = FALSE; + goto Done; + } + + // + // Retrieved inquiry data successfully + // + if (InquiryData->Peripheral_Qualifier != 0) { + ScsiDeviceFound = FALSE; + goto Done; + } + + if ((InquiryData->Peripheral_Type >= EFI_SCSI_TYPE_RESERVED_LOW) && + (InquiryData->Peripheral_Type <= EFI_SCSI_TYPE_RESERVED_HIGH)) { + ScsiDeviceFound = FALSE; + goto Done; + } + + // + // valid device type and peripheral qualifier combination. + // + ScsiIoDevice->ScsiDeviceType = InquiryData->Peripheral_Type; + ScsiIoDevice->RemovableDevice = InquiryData->Rmb; + if (InquiryData->Version == 0) { + ScsiIoDevice->ScsiVersion = 0; + } else { + // + // ANSI-approved version + // + ScsiIoDevice->ScsiVersion = (UINT8) (InquiryData->Version & 0x07); + } + + ScsiDeviceFound = TRUE; + +Done: + FreeAlignedBuffer (SenseData, sizeof (EFI_SCSI_SENSE_DATA)); + FreeAlignedBuffer (InquiryData, sizeof (EFI_SCSI_INQUIRY_DATA)); + + return ScsiDeviceFound; +} + + +/** + Convert EFI_SCSI_IO_SCSI_REQUEST_PACKET packet to EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet. + + @param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET + @param CommandPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + +**/ +EFI_STATUS +EFIAPI +ScsiioToPassThruPacket ( + IN EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, + OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *CommandPacket + ) +{ + // + //EFI 1.10 doesn't support Bi-Direction Command. + // + if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_BIDIRECTIONAL) { + return EFI_UNSUPPORTED; + } + + ZeroMem (CommandPacket, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); + + CommandPacket->Timeout = Packet->Timeout; + CommandPacket->Cdb = Packet->Cdb; + CommandPacket->CdbLength = Packet->CdbLength; + CommandPacket->DataDirection = Packet->DataDirection; + CommandPacket->HostAdapterStatus = Packet->HostAdapterStatus; + CommandPacket->TargetStatus = Packet->TargetStatus; + CommandPacket->SenseData = Packet->SenseData; + CommandPacket->SenseDataLength = Packet->SenseDataLength; + + if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_READ) { + CommandPacket->DataBuffer = Packet->InDataBuffer; + CommandPacket->TransferLength = Packet->InTransferLength; + } else if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_WRITE) { + CommandPacket->DataBuffer = Packet->OutDataBuffer; + CommandPacket->TransferLength = Packet->OutTransferLength; + } + return EFI_SUCCESS; +} + + +/** + Convert EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet to EFI_SCSI_IO_SCSI_REQUEST_PACKET packet. + + @param ScsiPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET + @param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET + +**/ +EFI_STATUS +EFIAPI +PassThruToScsiioPacket ( + IN EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket, + OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet + ) +{ + Packet->Timeout = ScsiPacket->Timeout; + Packet->Cdb = ScsiPacket->Cdb; + Packet->CdbLength = ScsiPacket->CdbLength; + Packet->DataDirection = ScsiPacket->DataDirection; + Packet->HostAdapterStatus = ScsiPacket->HostAdapterStatus; + Packet->TargetStatus = ScsiPacket->TargetStatus; + Packet->SenseData = ScsiPacket->SenseData; + Packet->SenseDataLength = ScsiPacket->SenseDataLength; + + if (ScsiPacket->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_READ) { + Packet->InDataBuffer = ScsiPacket->DataBuffer; + Packet->InTransferLength = ScsiPacket->TransferLength; + } else if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_WRITE) { + Packet->OutDataBuffer = ScsiPacket->DataBuffer; + Packet->OutTransferLength = ScsiPacket->TransferLength; + } + + return EFI_SUCCESS; +} + +/** + Notify Function in which convert EFI1.0 PassThru Packet back to UEF2.0 + SCSI IO Packet. + + @param Event The instance of EFI_EVENT. + @param Context The parameter passed in. + +**/ +VOID +EFIAPI +NotifyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet; + EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket; + EFI_EVENT CallerEvent; + SCSI_EVENT_DATA *PassData; + + PassData = (SCSI_EVENT_DATA*)Context; + Packet = (EFI_SCSI_IO_SCSI_REQUEST_PACKET *)PassData->Data1; + ScsiPacket = (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET*)mWorkingBuffer; + + // + // Convert EFI1.0 PassThru packet to UEFI2.0 SCSI IO Packet. + // + PassThruToScsiioPacket(ScsiPacket, Packet); + + // + // After converting EFI1.0 PassThru Packet back to UEFI2.0 SCSI IO Packet, + // free mWorkingBuffer. + // + gBS->FreePool(mWorkingBuffer); + + // + // Signal Event to tell caller to pick up UEFI2.0 SCSI IO Packet. + // + CallerEvent = PassData->Data2; + gBS->CloseEvent(Event); + gBS->SignalEvent(CallerEvent); +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.h b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.h new file mode 100644 index 000000000..97581866b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.h @@ -0,0 +1,486 @@ +/** @file + Header file for SCSI Bus Driver. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SCSI_BUS_H_ +#define _SCSI_BUS_H_ + + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SCSI_IO_DEV_SIGNATURE SIGNATURE_32 ('s', 'c', 'i', 'o') + +typedef union { + UINT32 Scsi; + UINT8 ExtScsi[4]; +} SCSI_ID; + +typedef struct _SCSI_TARGET_ID { + SCSI_ID ScsiId; + UINT8 ExtScsiId[12]; +}SCSI_TARGET_ID; + + +typedef struct { + VOID *Data1; + VOID *Data2; +} SCSI_EVENT_DATA; + +// +// SCSI Bus Controller device structure +// +#define SCSI_BUS_DEVICE_SIGNATURE SIGNATURE_32 ('s', 'c', 's', 'i') + +// +// SCSI Bus Timeout Experience Value +// +#define SCSI_BUS_TIMEOUT EFI_TIMER_PERIOD_SECONDS (3) + +// +// The ScsiBusProtocol is just used to locate ScsiBusDev +// structure in the SCSIBusDriverBindingStop(). Then we can +// Close all opened protocols and release this structure. +// ScsiBusProtocol is the private protocol. +// gEfiCallerIdGuid will be used as its protocol guid. +// +typedef struct _EFI_SCSI_BUS_PROTOCOL { + UINT64 Reserved; +} EFI_SCSI_BUS_PROTOCOL; + +typedef struct _SCSI_BUS_DEVICE { + UINTN Signature; + EFI_SCSI_BUS_PROTOCOL BusIdentify; + BOOLEAN ExtScsiSupport; + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiInterface; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiInterface; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; +} SCSI_BUS_DEVICE; + +#define SCSI_BUS_CONTROLLER_DEVICE_FROM_THIS(a) CR (a, SCSI_BUS_DEVICE, BusIdentify, SCSI_BUS_DEVICE_SIGNATURE) + +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_SCSI_IO_PROTOCOL ScsiIo; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + BOOLEAN ExtScsiSupport; + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; + SCSI_BUS_DEVICE *ScsiBusDeviceData; + SCSI_TARGET_ID Pun; + UINT64 Lun; + UINT8 ScsiDeviceType; + UINT8 ScsiVersion; + BOOLEAN RemovableDevice; +} SCSI_IO_DEV; + +#define SCSI_IO_DEV_FROM_THIS(a) CR (a, SCSI_IO_DEV, ScsiIo, SCSI_IO_DEV_SIGNATURE) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gScsiBusDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gScsiBusComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gScsiBusComponentName2; + +/** + Test to see if this driver supports ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Supported() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Start() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). + In order to make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() must follow these + calling restrictions. If any other agent wishes to call Stop() it must also + follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +SCSIBusDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Retrieves the device type information of the SCSI Controller. + + @param This Protocol instance pointer. + @param DeviceType A pointer to the device type information retrieved from + the SCSI Controller. + + @retval EFI_SUCCESS Retrieves the device type information successfully. + @retval EFI_INVALID_PARAMETER The DeviceType is NULL. + +**/ +EFI_STATUS +EFIAPI +ScsiGetDeviceType ( + IN EFI_SCSI_IO_PROTOCOL *This, + OUT UINT8 *DeviceType + ); + +/** + Retrieves the device location in the SCSI channel. + + @param This Protocol instance pointer. + @param Target A pointer to the Target ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of the SCSI device on + the SCSI channel. + + @retval EFI_SUCCESS Retrieves the device location successfully. + @retval EFI_INVALID_PARAMETER The Target or Lun is NULL. + +**/ +EFI_STATUS +EFIAPI +ScsiGetDeviceLocation ( + IN EFI_SCSI_IO_PROTOCOL *This, + IN OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets the SCSI Bus that the SCSI Controller is attached to. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The SCSI bus is reset successfully. + @retval EFI_DEVICE_ERROR Errors encountered when resetting the SCSI bus. + @retval EFI_UNSUPPORTED The bus reset operation is not supported by the + SCSI Host Controller. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset + the SCSI bus. +**/ +EFI_STATUS +EFIAPI +ScsiResetBus ( + IN EFI_SCSI_IO_PROTOCOL *This + ); + +/** + Resets the SCSI Controller that the device handle specifies. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS Reset the SCSI controller successfully. + @retval EFI_DEVICE_ERROR Errors are encountered when resetting the SCSI Controller. + @retval EFI_UNSUPPORTED The SCSI bus does not support a device reset operation. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the + SCSI Controller. +**/ +EFI_STATUS +EFIAPI +ScsiResetDevice ( + IN EFI_SCSI_IO_PROTOCOL *This + ); + +/** + Sends a SCSI Request Packet to the SCSI Controller for execution. + + @param This Protocol instance pointer. + @param CommandPacket The SCSI request packet to send to the SCSI + Controller specified by the device handle. + @param Event If the SCSI bus where the SCSI device is attached + does not support non-blocking I/O, then Event is + ignored, and blocking I/O is performed. + If Event is NULL, then blocking I/O is performed. + If Event is not NULL and non-blocking I/O is + supported, then non-blocking I/O is performed, + and Event will be signaled when the SCSI Request + Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host + successfully, and TransferLength bytes were + transferred to/from DataBuffer.See + HostAdapterStatus, TargetStatus, + SenseDataLength, and SenseData in that order + for additional status information. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was executed, + but the entire DataBuffer could not be transferred. + The actual number of bytes transferred is returned + in TransferLength. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because + there are too many SCSI Command Packets already + queued.The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SCSI Request Packet. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. + @retval EFI_INVALID_PARAMETER The contents of CommandPacket are invalid. + The SCSI Request Packet was not sent, so no + additional status information is available. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet + is not supported by the SCSI initiator(i.e., SCSI + Host Controller). The SCSI Request Packet was not + sent, so no additional status information is + available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI + Request Packet to execute. See HostAdapterStatus, + TargetStatus, SenseDataLength, and SenseData in + that order for additional status information. +**/ +EFI_STATUS +EFIAPI +ScsiExecuteSCSICommand ( + IN EFI_SCSI_IO_PROTOCOL *This, + IN OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Scan SCSI Bus to discover the device, and attach ScsiIoProtocol to it. + + @param This Protocol instance pointer + @param Controller Controller handle + @param TargetId Target to be scanned + @param Lun The Lun of the SCSI device on the SCSI channel. + @param ScsiBusDev The pointer of SCSI_BUS_DEVICE + + @retval EFI_SUCCESS Successfully to discover the device and attach + ScsiIoProtocol to it. + @retval EFI_OUT_OF_RESOURCES Fail to discover the device. + +**/ +EFI_STATUS +EFIAPI +ScsiScanCreateDevice ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN SCSI_TARGET_ID *TargetId, + IN UINT64 Lun, + IN OUT SCSI_BUS_DEVICE *ScsiBusDev + ); + +/** + Discovery SCSI Device + + @param ScsiIoDevice The pointer of SCSI_IO_DEV + + @retval TRUE Find SCSI Device and verify it. + @retval FALSE Unable to find SCSI Device. + +**/ +BOOLEAN +DiscoverScsiDevice ( + IN OUT SCSI_IO_DEV *ScsiIoDevice + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.uni b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.uni new file mode 100644 index 000000000..e384c82ea --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBus.uni @@ -0,0 +1,17 @@ +// /** @file +// The SCSI bus driver scans all SCSI devices and creates a device handle for each of them. +// +// Note that the driver will install the Device Path Protocol and SCSI I/O Protocol on +// these handles. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Scans all SCSI devices; Creates a device handle for each device" + +#string STR_MODULE_DESCRIPTION #language en-US "Note that the driver will install the Device Path Protocol and SCSI I/O Protocol on these handles." + diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf new file mode 100644 index 000000000..807ce1320 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf @@ -0,0 +1,64 @@ +## @file +# The SCSI bus driver scans all SCSI devices and creates a device handle for each of them. +# Note that the driver will install the Device Path Protocol and SCSI I/O Protocol on +# these handles. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ScsiBus + MODULE_UNI_FILE = ScsiBus.uni + FILE_GUID = 0167CCC4-D0F7-4f21-A3EF-9E64B7CDCE8B + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeScsiBus + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gSCSIBusDriverBinding +# COMPONENT_NAME = gScsiBusComponentName +# COMPONENT_NAME2 = gScsiBusComponentName2 +# + +[Sources] + ComponentName.c + ScsiBus.c + ScsiBus.h + + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + UefiScsiLib + BaseMemoryLib + UefiLib + UefiDriverEntryPoint + DebugLib + MemoryAllocationLib + ReportStatusCodeLib + + +[Protocols] + gEfiScsiIoProtocolGuid ## BY_START + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + gEfiScsiPassThruProtocolGuid ## TO_START + gEfiExtScsiPassThruProtocolGuid ## TO_START + +[UserExtensions.TianoCore."ExtraFiles"] + ScsiBusExtra.uni diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusExtra.uni b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusExtra.uni new file mode 100644 index 000000000..df8a2019a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// ScsiBus Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SCSI Bus DXE Driver" + + diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ComponentName.c new file mode 100644 index 000000000..b193de9ed --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ComponentName.c @@ -0,0 +1,218 @@ +/** @file + UEFI Component Name(2) protocol implementation for SCSI disk driver. + +Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "ScsiDisk.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gScsiDiskComponentName = { + ScsiDiskComponentNameGetDriverName, + ScsiDiskComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gScsiDiskComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ScsiDiskComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ScsiDiskComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mScsiDiskDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"Scsi Disk Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mScsiDiskDriverNameTable, + DriverName, + (BOOLEAN)(This == &gScsiDiskComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gScsiDiskDriverBinding.DriverBindingHandle, + &gEfiScsiIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the device context + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + gScsiDiskDriverBinding.DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlockIo); + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + ScsiDiskDevice->ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gScsiDiskComponentName) + ); + +} diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c new file mode 100644 index 000000000..c80e78fa8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c @@ -0,0 +1,6327 @@ +/** @file + SCSI disk driver that layers on every SCSI IO protocol in the system. + +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "ScsiDisk.h" + +EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = { + ScsiDiskDriverBindingSupported, + ScsiDiskDriverBindingStart, + ScsiDiskDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = { + EFI_DISK_INFO_SCSI_INTERFACE_GUID, + ScsiDiskInfoInquiry, + ScsiDiskInfoIdentify, + ScsiDiskInfoSenseData, + ScsiDiskInfoWhichIde +}; + +/** + Allocates an aligned buffer for SCSI disk. + + This function allocates an aligned buffer for the SCSI disk to perform + SCSI IO operations. The alignment requirement is from SCSI IO interface. + + @param ScsiDiskDevice The SCSI disk involved for the operation. + @param BufferSize The request buffer size. + + @return A pointer to the aligned buffer or NULL if the allocation fails. + +**/ +VOID * +AllocateAlignedBuffer ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINTN BufferSize + ) +{ + return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign); +} + +/** + Frees an aligned buffer for SCSI disk. + + This function frees an aligned buffer for the SCSI disk to perform + SCSI IO operations. + + @param Buffer The aligned buffer to be freed. + @param BufferSize The request buffer size. + +**/ +VOID +FreeAlignedBuffer ( + IN VOID *Buffer, + IN UINTN BufferSize + ) +{ + if (Buffer != NULL) { + FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize)); + } +} + +/** + The user Entry Point for module ScsiDisk. + + The user code starts with this function. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeScsiDisk( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gScsiDiskDriverBinding, + ImageHandle, + &gScsiDiskComponentName, + &gScsiDiskComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. + If any other agent wishes to call Supported() it must also follow these + calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SCSI_IO_PROTOCOL *ScsiIo; + UINT8 DeviceType; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + (VOID **) &ScsiIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType); + if (!EFI_ERROR (Status)) { + if ((DeviceType == EFI_SCSI_TYPE_DISK) || + (DeviceType == EFI_SCSI_TYPE_CDROM) || + (DeviceType == EFI_SCSI_TYPE_WLUN)) { + Status = EFI_SUCCESS; + } else { + Status = EFI_UNSUPPORTED; + } + } + + gBS->CloseProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; +} + + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Start() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_SCSI_IO_PROTOCOL *ScsiIo; + SCSI_DISK_DEV *ScsiDiskDevice; + BOOLEAN Temp; + UINT8 Index; + UINT8 MaxRetry; + BOOLEAN NeedRetry; + BOOLEAN MustReadCapacity; + + MustReadCapacity = TRUE; + + ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV)); + if (ScsiDiskDevice == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + (VOID **) &ScsiIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + FreePool (ScsiDiskDevice); + return Status; + } + + ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE; + ScsiDiskDevice->ScsiIo = ScsiIo; + ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3; + ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia; + ScsiDiskDevice->BlkIo.Media->IoAlign = ScsiIo->IoAlign; + ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset; + ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks; + ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks; + ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks; + ScsiDiskDevice->BlkIo2.Media = &ScsiDiskDevice->BlkIoMedia; + ScsiDiskDevice->BlkIo2.Reset = ScsiDiskResetEx; + ScsiDiskDevice->BlkIo2.ReadBlocksEx = ScsiDiskReadBlocksEx; + ScsiDiskDevice->BlkIo2.WriteBlocksEx = ScsiDiskWriteBlocksEx; + ScsiDiskDevice->BlkIo2.FlushBlocksEx = ScsiDiskFlushBlocksEx; + ScsiDiskDevice->StorageSecurity.ReceiveData = ScsiDiskReceiveData; + ScsiDiskDevice->StorageSecurity.SendData = ScsiDiskSendData; + ScsiDiskDevice->EraseBlock.Revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION; + ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1; + ScsiDiskDevice->EraseBlock.EraseBlocks = ScsiDiskEraseBlocks; + ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt = 1; + ScsiDiskDevice->BlockLimitsVpdSupported = FALSE; + ScsiDiskDevice->Handle = Controller; + InitializeListHead (&ScsiDiskDevice->AsyncTaskQueue); + + ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType)); + switch (ScsiDiskDevice->DeviceType) { + case EFI_SCSI_TYPE_DISK: + ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200; + MustReadCapacity = TRUE; + break; + + case EFI_SCSI_TYPE_CDROM: + ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800; + ScsiDiskDevice->BlkIo.Media->ReadOnly = TRUE; + MustReadCapacity = FALSE; + break; + + case EFI_SCSI_TYPE_WLUN: + MustReadCapacity = FALSE; + break; + } + // + // The Sense Data Array's initial size is 6 + // + ScsiDiskDevice->SenseDataNumber = 6; + ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool ( + sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber + ); + if (ScsiDiskDevice->SenseData == NULL) { + gBS->CloseProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + FreePool (ScsiDiskDevice); + return EFI_OUT_OF_RESOURCES; + } + + // + // Retrieve device information + // + MaxRetry = 2; + for (Index = 0; Index < MaxRetry; Index++) { + Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry); + if (!EFI_ERROR (Status)) { + break; + } + + if (!NeedRetry) { + FreePool (ScsiDiskDevice->SenseData); + gBS->CloseProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + FreePool (ScsiDiskDevice); + return EFI_DEVICE_ERROR; + } + } + // + // The second parameter "TRUE" means must + // retrieve media capacity + // + Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp); + if (!EFI_ERROR (Status)) { + // + // Determine if Block IO & Block IO2 should be produced on this controller + // handle + // + if (DetermineInstallBlockIo (Controller)) { + InitializeInstallDiskInfo (ScsiDiskDevice, Controller); + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &gEfiDiskInfoProtocolGuid, + &ScsiDiskDevice->DiskInfo, + NULL + ); + if (!EFI_ERROR (Status)) { + if (DetermineInstallEraseBlock (ScsiDiskDevice, Controller)) { + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiEraseBlockProtocolGuid, + EFI_NATIVE_INTERFACE, + &ScsiDiskDevice->EraseBlock + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Erase Block Protocol! Status = %r\n", Status)); + } + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, Controller)) { + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiStorageSecurityCommandProtocolGuid, + EFI_NATIVE_INTERFACE, + &ScsiDiskDevice->StorageSecurity + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Storage Security Command Protocol! Status = %r\n", Status)); + } + } + ScsiDiskDevice->ControllerNameTable = NULL; + AddUnicodeString2 ( + "eng", + gScsiDiskComponentName.SupportedLanguages, + &ScsiDiskDevice->ControllerNameTable, + L"SCSI Disk Device", + TRUE + ); + AddUnicodeString2 ( + "en", + gScsiDiskComponentName2.SupportedLanguages, + &ScsiDiskDevice->ControllerNameTable, + L"SCSI Disk Device", + FALSE + ); + return EFI_SUCCESS; + } + } + } + + gBS->FreePool (ScsiDiskDevice->SenseData); + gBS->FreePool (ScsiDiskDevice); + gBS->CloseProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + +} + + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). + In order to make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() must follow these + calling restrictions. If any other agent wishes to call Stop() it must + also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_BLOCK_IO_PROTOCOL *BlkIo; + EFI_ERASE_BLOCK_PROTOCOL *EraseBlock; + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlkIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlkIo); + + // + // Wait for the BlockIo2 requests queue to become empty + // + while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)); + + // + // If Erase Block Protocol is installed, then uninstall this protocol. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiEraseBlockProtocolGuid, + (VOID **) &EraseBlock, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &gEfiDiskInfoProtocolGuid, + &ScsiDiskDevice->DiskInfo, + NULL + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiScsiIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + ReleaseScsiDiskDeviceResources (ScsiDiskDevice); + + return EFI_SUCCESS; + } + // + // errors met + // + return Status; +} + +/** + Reset SCSI Disk. + + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + @param ExtendedVerification The flag about if extend verificate + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice(). + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_TPL OldTpl; + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_STATUS Status; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This); + + Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + + if (EFI_ERROR (Status)) { + if (Status == EFI_UNSUPPORTED) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + goto Done; + } + } + + if (!ExtendedVerification) { + goto Done; + } + + Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is to Read Block from SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected + @param Lba The logic block address + @param BufferSize The size of Buffer + @param Buffer The buffer to fill the read out data + + @retval EFI_SUCCESS Successfully to read out block. + @retval EFI_DEVICE_ERROR Fail to detect media. + @retval EFI_NO_MEDIA Media is not present. + @retval EFI_MEDIA_CHANGED Media has changed. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER Invalid parameter passed in. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This); + Media = ScsiDiskDevice->BlkIo.Media; + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + // + // Get the intrinsic block size + // + BlockSize = Media->BlockSize; + + if (BlockSize == 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + NumberOfBlocks = BufferSize / BlockSize; + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (MediaId != Media->MediaId) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Buffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (BufferSize == 0) { + Status = EFI_SUCCESS; + goto Done; + } + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + if (Lba > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // If all the parameters are valid, then perform read sectors command + // to transfer data from device to host. + // + Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks); + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is to Write Block to SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + @param MediaId The Id of Media detected + @param Lba The logic block address + @param BufferSize The size of Buffer + @param Buffer The buffer to fill the read out data + + @retval EFI_SUCCESS Successfully to read out block. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR Fail to detect media. + @retval EFI_NO_MEDIA Media is not present. + @retval EFI_MEDIA_CHANGED Media has changed. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER Invalid parameter passed in. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This); + Media = ScsiDiskDevice->BlkIo.Media; + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + // + // Get the intrinsic block size + // + BlockSize = Media->BlockSize; + + if (BlockSize == 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + NumberOfBlocks = BufferSize / BlockSize; + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (MediaId != Media->MediaId) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Media->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + + if (BufferSize == 0) { + Status = EFI_SUCCESS; + goto Done; + } + + if (Buffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + if (Lba > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + // + // if all the parameters are valid, then perform read sectors command + // to transfer data from device to host. + // + Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks); + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Flush Block to Disk. + + EFI_SUCCESS is returned directly. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + + @retval EFI_SUCCESS All outstanding data was written to the device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + // + // return directly + // + return EFI_SUCCESS; +} + + +/** + Reset SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO2_PROTOCOL. + @param ExtendedVerification The flag about if extend verificate. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice(). + +**/ +EFI_STATUS +EFIAPI +ScsiDiskResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_TPL OldTpl; + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_STATUS Status; + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This); + + Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + + if (EFI_ERROR (Status)) { + if (Status == EFI_UNSUPPORTED) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + goto Done; + } + } + + if (!ExtendedVerification) { + goto Done; + } + + Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is to Read Block from SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected. + @param Lba The logic block address. + @param Token A pointer to the token associated with the transaction. + @param BufferSize The size of Buffer. + @param Buffer The buffer to fill the read out data. + + @retval EFI_SUCCESS The read request was queued if Token-> Event is + not NULL. The data was read correctly from the + device if theToken-> Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This); + Media = ScsiDiskDevice->BlkIo.Media; + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + // + // Get the intrinsic block size + // + BlockSize = Media->BlockSize; + + if (BlockSize == 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + NumberOfBlocks = BufferSize / BlockSize; + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (MediaId != Media->MediaId) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Buffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (BufferSize == 0) { + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + Status = EFI_SUCCESS; + goto Done; + } + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + if (Lba > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // If all the parameters are valid, then perform read sectors command + // to transfer data from device to host. + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + Status = ScsiDiskAsyncReadSectors ( + ScsiDiskDevice, + Buffer, + Lba, + NumberOfBlocks, + Token + ); + } else { + Status = ScsiDiskReadSectors ( + ScsiDiskDevice, + Buffer, + Lba, + NumberOfBlocks + ); + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + The function is to Write Block to SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected. + @param Lba The logic block address. + @param Token A pointer to the token associated with the transaction. + @param BufferSize The size of Buffer. + @param Buffer The buffer to fill the read out data. + + @retval EFI_SUCCESS The data were written correctly to the device. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the write operation. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This); + Media = ScsiDiskDevice->BlkIo.Media; + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + // + // Get the intrinsic block size + // + BlockSize = Media->BlockSize; + + if (BlockSize == 0) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + NumberOfBlocks = BufferSize / BlockSize; + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (MediaId != Media->MediaId) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Media->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + + if (BufferSize == 0) { + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + Status = EFI_SUCCESS; + goto Done; + } + + if (Buffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Done; + } + + if (Lba > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + // + // if all the parameters are valid, then perform write sectors command + // to transfer data from device to host. + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + Status = ScsiDiskAsyncWriteSectors ( + ScsiDiskDevice, + Buffer, + Lba, + NumberOfBlocks, + Token + ); + } else { + Status = ScsiDiskWriteSectors ( + ScsiDiskDevice, + Buffer, + Lba, + NumberOfBlocks + ); + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to + write data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This); + Media = ScsiDiskDevice->BlkIo.Media; + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (Media->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + + // + // Wait for the BlockIo2 requests queue to become empty + // + while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)); + + Status = EFI_SUCCESS; + + // + // Signal caller event + // + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Internal helper notify function which process the result of an asynchronous + SCSI UNMAP Command and signal the event passed from EraseBlocks. + + @param Event The instance of EFI_EVENT. + @param Context The parameter passed in. + +**/ +VOID +EFIAPI +ScsiDiskAsyncUnmapNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + SCSI_ERASEBLK_REQUEST *EraseBlkReq; + EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket; + EFI_ERASE_BLOCK_TOKEN *Token; + EFI_STATUS Status; + + gBS->CloseEvent (Event); + + EraseBlkReq = (SCSI_ERASEBLK_REQUEST *) Context; + CommandPacket = &EraseBlkReq->CommandPacket; + Token = EraseBlkReq->Token; + Token->TransactionStatus = EFI_SUCCESS; + + Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_ERROR, + "ScsiDiskAsyncUnmapNotify: Host adapter indicating error status 0x%x.\n", + CommandPacket->HostAdapterStatus + )); + + Token->TransactionStatus = Status; + goto Done; + } + + Status = CheckTargetStatus (CommandPacket->TargetStatus); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_ERROR, + "ScsiDiskAsyncUnmapNotify: Target indicating error status 0x%x.\n", + CommandPacket->HostAdapterStatus + )); + + Token->TransactionStatus = Status; + goto Done; + } + +Done: + RemoveEntryList (&EraseBlkReq->Link); + FreePool (CommandPacket->OutDataBuffer); + FreePool (EraseBlkReq->CommandPacket.Cdb); + FreePool (EraseBlkReq); + + gBS->SignalEvent (Token->Event); +} + +/** + Require the device server to cause one or more LBAs to be unmapped. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Lba The start block number. + @param Blocks Total block number to be unmapped. + @param Token The pointer to the token associated with the + non-blocking erase block request. + + @retval EFI_SUCCESS Target blocks have been successfully unmapped. + @retval EFI_DEVICE_ERROR Fail to unmap the target blocks. + +**/ +EFI_STATUS +ScsiDiskUnmap ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Lba, + IN UINTN Blocks, + IN EFI_ERASE_BLOCK_TOKEN *Token OPTIONAL + ) +{ + EFI_SCSI_IO_PROTOCOL *ScsiIo; + SCSI_ERASEBLK_REQUEST *EraseBlkReq; + EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket; + EFI_SCSI_DISK_UNMAP_BLOCK_DESP *BlkDespPtr; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINT8 *Cdb; + UINT32 MaxLbaCnt; + UINT32 MaxBlkDespCnt; + UINT32 BlkDespCnt; + UINT16 UnmapParamListLen; + VOID *UnmapParamList; + EFI_EVENT AsyncUnmapEvent; + EFI_TPL OldTpl; + + ScsiIo = ScsiDiskDevice->ScsiIo; + MaxLbaCnt = ScsiDiskDevice->UnmapInfo.MaxLbaCnt; + MaxBlkDespCnt = ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt; + EraseBlkReq = NULL; + UnmapParamList = NULL; + AsyncUnmapEvent = NULL; + ReturnStatus = EFI_SUCCESS; + + if (Blocks / (UINTN) MaxLbaCnt > MaxBlkDespCnt) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + EraseBlkReq = AllocateZeroPool (sizeof (SCSI_ERASEBLK_REQUEST)); + if (EraseBlkReq == NULL) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + EraseBlkReq->CommandPacket.Cdb = AllocateZeroPool (0xA); + if (EraseBlkReq->CommandPacket.Cdb == NULL) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + BlkDespCnt = (UINT32) ((Blocks - 1) / MaxLbaCnt + 1); + UnmapParamListLen = (UINT16) (sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER) + + BlkDespCnt * sizeof (EFI_SCSI_DISK_UNMAP_BLOCK_DESP)); + UnmapParamList = AllocateZeroPool (UnmapParamListLen); + if (UnmapParamList == NULL) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + *((UINT16 *)UnmapParamList) = SwapBytes16 (UnmapParamListLen - 2); + *((UINT16 *)UnmapParamList + 1) = SwapBytes16 (UnmapParamListLen - sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)); + + BlkDespPtr = (EFI_SCSI_DISK_UNMAP_BLOCK_DESP *)((UINT8 *)UnmapParamList + sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)); + while (Blocks > 0) { + if (Blocks > MaxLbaCnt) { + *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba); + *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 (MaxLbaCnt); + Blocks -= MaxLbaCnt; + Lba += MaxLbaCnt; + } else { + *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba); + *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 ((UINT32) Blocks); + Blocks = 0; + } + + BlkDespPtr++; + } + + CommandPacket = &EraseBlkReq->CommandPacket; + CommandPacket->Timeout = SCSI_DISK_TIMEOUT; + CommandPacket->OutDataBuffer = UnmapParamList; + CommandPacket->OutTransferLength = UnmapParamListLen; + CommandPacket->CdbLength = 0xA; + CommandPacket->DataDirection = EFI_SCSI_DATA_OUT; + // + // Fill Cdb for UNMAP Command + // + Cdb = CommandPacket->Cdb; + Cdb[0] = EFI_SCSI_OP_UNMAP; + WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 (UnmapParamListLen)); + + if ((Token != NULL) && (Token->Event != NULL)) { + // + // Non-blocking UNMAP request + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ScsiDiskAsyncUnmapNotify, + EraseBlkReq, + &AsyncUnmapEvent + ); + if (EFI_ERROR(Status)) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &EraseBlkReq->Link); + gBS->RestoreTPL (OldTpl); + + EraseBlkReq->Token = Token; + + Status = ScsiIo->ExecuteScsiCommand ( + ScsiIo, + CommandPacket, + AsyncUnmapEvent + ); + if (EFI_ERROR(Status)) { + ReturnStatus = EFI_DEVICE_ERROR; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlkReq->Link); + gBS->RestoreTPL (OldTpl); + + goto Done; + } else { + // + // Directly return if the non-blocking UNMAP request is queued. + // + return EFI_SUCCESS; + } + } else { + // + // Blocking UNMAP request + // + Status = ScsiIo->ExecuteScsiCommand ( + ScsiIo, + CommandPacket, + NULL + ); + if (EFI_ERROR(Status)) { + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + } + + // + // Only blocking UNMAP request will reach here. + // + Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_ERROR, + "ScsiDiskUnmap: Host adapter indicating error status 0x%x.\n", + CommandPacket->HostAdapterStatus + )); + + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + + Status = CheckTargetStatus (CommandPacket->TargetStatus); + if (EFI_ERROR(Status)) { + DEBUG (( + EFI_D_ERROR, + "ScsiDiskUnmap: Target indicating error status 0x%x.\n", + CommandPacket->HostAdapterStatus + )); + + ReturnStatus = EFI_DEVICE_ERROR; + goto Done; + } + +Done: + if (EraseBlkReq != NULL) { + if (EraseBlkReq->CommandPacket.Cdb != NULL) { + FreePool (EraseBlkReq->CommandPacket.Cdb); + } + FreePool (EraseBlkReq); + } + + if (UnmapParamList != NULL) { + FreePool (UnmapParamList); + } + + if (AsyncUnmapEvent != NULL) { + gBS->CloseEvent (AsyncUnmapEvent); + } + + return ReturnStatus; +} + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + UINTN BlockSize; + UINTN NumberOfBlocks; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + + MediaChange = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_ERASEBLK (This); + + if (!IS_DEVICE_FIXED(ScsiDiskDevice)) { + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + Status = EFI_MEDIA_CHANGED; + goto Done; + } + } + // + // Get the intrinsic block size + // + Media = ScsiDiskDevice->BlkIo.Media; + + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if (MediaId != Media->MediaId) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Media->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + + if (Size == 0) { + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + Status = EFI_SUCCESS; + goto Done; + } + + BlockSize = Media->BlockSize; + if ((Size % BlockSize) != 0) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + NumberOfBlocks = Size / BlockSize; + if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, Token); + } else { + Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, NULL); + } + +Done: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId OPTIONAL, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + VOID *AlignedBuffer; + BOOLEAN AlignedBufferAllocated; + + AlignedBuffer = NULL; + MediaChange = FALSE; + AlignedBufferAllocated = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_STORSEC (This); + Media = ScsiDiskDevice->BlkIo.Media; + + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + + if (!IS_DEVICE_FIXED (ScsiDiskDevice)) { + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + + // + // Validate Media + // + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if ((MediaId != 0) && (MediaId != Media->MediaId)) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (PayloadBufferSize != 0) { + if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL)) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) { + AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize); + if (AlignedBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + ZeroMem (AlignedBuffer, PayloadBufferSize); + AlignedBufferAllocated = TRUE; + } else { + AlignedBuffer = PayloadBuffer; + } + } + + Status = ScsiSecurityProtocolInCommand ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + SecurityProtocolId, + SecurityProtocolSpecificData, + FALSE, + PayloadBufferSize, + AlignedBuffer, + PayloadTransferSize + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (AlignedBufferAllocated) { + CopyMem (PayloadBuffer, AlignedBuffer, PayloadBufferSize); + } + + if (PayloadBufferSize < *PayloadTransferSize) { + Status = EFI_WARN_BUFFER_TOO_SMALL; + goto Done; + } + + Status = CheckHostAdapterStatus (HostAdapterStatus); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = CheckTargetStatus (TargetStatus); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if (AlignedBufferAllocated) { + ZeroMem (AlignedBuffer, PayloadBufferSize); + FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize); + } + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskSendData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId OPTIONAL, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + BOOLEAN MediaChange; + EFI_TPL OldTpl; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + VOID *AlignedBuffer; + BOOLEAN AlignedBufferAllocated; + + AlignedBuffer = NULL; + MediaChange = FALSE; + AlignedBufferAllocated = FALSE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + ScsiDiskDevice = SCSI_DISK_DEV_FROM_STORSEC (This); + Media = ScsiDiskDevice->BlkIo.Media; + + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + + if (!IS_DEVICE_FIXED (ScsiDiskDevice)) { + Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Done; + } + + if (MediaChange) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIoProtocolGuid, + &ScsiDiskDevice->BlkIo, + &ScsiDiskDevice->BlkIo + ); + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiBlockIo2ProtocolGuid, + &ScsiDiskDevice->BlkIo2, + &ScsiDiskDevice->BlkIo2 + ); + if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiEraseBlockProtocolGuid, + &ScsiDiskDevice->EraseBlock, + &ScsiDiskDevice->EraseBlock + ); + } + if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) { + gBS->ReinstallProtocolInterface ( + ScsiDiskDevice->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + &ScsiDiskDevice->StorageSecurity, + &ScsiDiskDevice->StorageSecurity + ); + } + if (Media->MediaPresent) { + Status = EFI_MEDIA_CHANGED; + } else { + Status = EFI_NO_MEDIA; + } + goto Done; + } + } + + // + // Validate Media + // + if (!(Media->MediaPresent)) { + Status = EFI_NO_MEDIA; + goto Done; + } + + if ((MediaId != 0) && (MediaId != Media->MediaId)) { + Status = EFI_MEDIA_CHANGED; + goto Done; + } + + if (Media->ReadOnly) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + + if (PayloadBufferSize != 0) { + if (PayloadBuffer == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) { + AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize); + if (AlignedBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + CopyMem (AlignedBuffer, PayloadBuffer, PayloadBufferSize); + AlignedBufferAllocated = TRUE; + } else { + AlignedBuffer = PayloadBuffer; + } + } + + Status = ScsiSecurityProtocolOutCommand ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + SecurityProtocolId, + SecurityProtocolSpecificData, + FALSE, + PayloadBufferSize, + AlignedBuffer + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = CheckHostAdapterStatus (HostAdapterStatus); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = CheckTargetStatus (TargetStatus); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if (AlignedBufferAllocated) { + ZeroMem (AlignedBuffer, PayloadBufferSize); + FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize); + } + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Detect Device and read out capacity ,if error occurs, parse the sense key. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param MustReadCapacity The flag about reading device capacity + @param MediaChange The pointer of flag indicates if media has changed + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to detect media + +**/ +EFI_STATUS +ScsiDiskDetectMedia ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN BOOLEAN MustReadCapacity, + OUT BOOLEAN *MediaChange + ) +{ + EFI_STATUS Status; + EFI_SCSI_SENSE_DATA *SenseData; + UINTN NumberOfSenseKeys; + BOOLEAN NeedRetry; + BOOLEAN NeedReadCapacity; + UINT8 Retry; + UINT8 MaxRetry; + EFI_BLOCK_IO_MEDIA OldMedia; + UINTN Action; + EFI_EVENT TimeoutEvt; + + Status = EFI_SUCCESS; + SenseData = NULL; + NumberOfSenseKeys = 0; + Retry = 0; + MaxRetry = 3; + Action = ACTION_NO_ACTION; + NeedReadCapacity = FALSE; + *MediaChange = FALSE; + TimeoutEvt = NULL; + + CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia)); + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvt + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120)); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Sending Test_Unit cmd to poll device status. + // If the sense data shows the drive is not ready or reset before, we need poll the device status again. + // We limit the upper boundary to 120 seconds. + // + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) { + Status = ScsiDiskTestUnitReady ( + ScsiDiskDevice, + &NeedRetry, + &SenseData, + &NumberOfSenseKeys + ); + if (!EFI_ERROR (Status)) { + Status = DetectMediaParsingSenseKeys ( + ScsiDiskDevice, + SenseData, + NumberOfSenseKeys, + &Action + ); + if (EFI_ERROR (Status)) { + goto EXIT; + } else if (Action == ACTION_RETRY_COMMAND_LATER) { + continue; + } else { + break; + } + } else { + Retry++; + if (!NeedRetry || (Retry >= MaxRetry)) { + goto EXIT; + } + } + } + + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // ACTION_NO_ACTION: need not read capacity + // other action code: need read capacity + // + if (Action == ACTION_READ_CAPACITY) { + NeedReadCapacity = TRUE; + } + + // + // READ_CAPACITY command is not supported by any of the UFS WLUNs. + // + if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) { + NeedReadCapacity = FALSE; + MustReadCapacity = FALSE; + ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE; + } + + // + // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE, + // retrieve capacity via Read Capacity command + // + if (NeedReadCapacity || MustReadCapacity) { + // + // retrieve media information + // + for (Retry = 0; Retry < MaxRetry; Retry++) { + Status = ScsiDiskReadCapacity ( + ScsiDiskDevice, + &NeedRetry, + &SenseData, + &NumberOfSenseKeys + ); + if (!EFI_ERROR (Status)) { + // + // analyze sense key to action + // + Status = DetectMediaParsingSenseKeys ( + ScsiDiskDevice, + SenseData, + NumberOfSenseKeys, + &Action + ); + if (EFI_ERROR (Status)) { + // + // if Status is error, it may indicate crisis error, + // so return without retry. + // + goto EXIT; + } else if (Action == ACTION_RETRY_COMMAND_LATER) { + Retry = 0; + continue; + } else { + break; + } + } else { + Retry++; + if (!NeedRetry || (Retry >= MaxRetry)) { + goto EXIT; + } + } + } + + if (EFI_ERROR (Status)) { + goto EXIT; + } + } + + if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) { + // + // Media change information got from the device + // + *MediaChange = TRUE; + } + + if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) { + *MediaChange = TRUE; + ScsiDiskDevice->BlkIo.Media->MediaId += 1; + } + + if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) { + *MediaChange = TRUE; + ScsiDiskDevice->BlkIo.Media->MediaId += 1; + } + + if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) { + *MediaChange = TRUE; + ScsiDiskDevice->BlkIo.Media->MediaId += 1; + } + + if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) { + if (ScsiDiskDevice->BlkIo.Media->MediaPresent) { + // + // when change from no media to media present, reset the MediaId to 1. + // + ScsiDiskDevice->BlkIo.Media->MediaId = 1; + } else { + // + // when no media, reset the MediaId to zero. + // + ScsiDiskDevice->BlkIo.Media->MediaId = 0; + } + + *MediaChange = TRUE; + } + +EXIT: + if (TimeoutEvt != NULL) { + gBS->CloseEvent (TimeoutEvt); + } + return Status; +} + + +/** + Send out Inquiry command to Device. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry Indicates if needs try again when error happens + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to detect media + +**/ +EFI_STATUS +ScsiDiskInquiryDevice ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry + ) +{ + UINT32 InquiryDataLength; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + EFI_SCSI_SENSE_DATA *SenseDataArray; + UINTN NumberOfSenseKeys; + EFI_STATUS Status; + UINT8 MaxRetry; + UINT8 Index; + EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages; + EFI_SCSI_BLOCK_LIMITS_VPD_PAGE *BlockLimits; + UINTN PageLength; + + InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA); + SenseDataLength = 0; + + Status = ScsiInquiryCommand ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) &(ScsiDiskDevice->InquiryData), + &InquiryDataLength, + FALSE + ); + // + // no need to check HostAdapterStatus and TargetStatus + // + if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) { + ParseInquiryData (ScsiDiskDevice); + + if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) { + // + // Check whether the device supports Block Limits VPD page (0xB0) + // + SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE)); + if (SupportedVpdPages == NULL) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE)); + InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE); + SenseDataLength = 0; + Status = ScsiInquiryCommandEx ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) SupportedVpdPages, + &InquiryDataLength, + TRUE, + EFI_SCSI_PAGE_CODE_SUPPORTED_VPD + ); + if (!EFI_ERROR (Status)) { + PageLength = (SupportedVpdPages->PageLength2 << 8) + | SupportedVpdPages->PageLength1; + + // + // Sanity checks for coping with broken devices + // + if (PageLength > sizeof SupportedVpdPages->SupportedVpdPageList) { + DEBUG ((EFI_D_WARN, + "%a: invalid PageLength (%u) in Supported VPD Pages page\n", + __FUNCTION__, (UINT32)PageLength)); + PageLength = 0; + } + + if ((PageLength > 0) && + (SupportedVpdPages->SupportedVpdPageList[0] != + EFI_SCSI_PAGE_CODE_SUPPORTED_VPD)) { + DEBUG ((EFI_D_WARN, + "%a: Supported VPD Pages page doesn't start with code 0x%02x\n", + __FUNCTION__, EFI_SCSI_PAGE_CODE_SUPPORTED_VPD)); + PageLength = 0; + } + + // + // Locate the code for the Block Limits VPD page + // + for (Index = 0; Index < PageLength; Index++) { + // + // Sanity check + // + if ((Index > 0) && + (SupportedVpdPages->SupportedVpdPageList[Index] <= + SupportedVpdPages->SupportedVpdPageList[Index - 1])) { + DEBUG ((EFI_D_WARN, + "%a: non-ascending code in Supported VPD Pages page @ %u\n", + __FUNCTION__, Index)); + Index = 0; + PageLength = 0; + break; + } + + if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) { + break; + } + } + + // + // Query the Block Limits VPD page + // + if (Index < PageLength) { + BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE)); + if (BlockLimits == NULL) { + FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE)); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE)); + InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE); + SenseDataLength = 0; + Status = ScsiInquiryCommandEx ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) BlockLimits, + &InquiryDataLength, + TRUE, + EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD + ); + if (!EFI_ERROR (Status)) { + ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity = + (BlockLimits->OptimalTransferLengthGranularity2 << 8) | + BlockLimits->OptimalTransferLengthGranularity1; + + ScsiDiskDevice->UnmapInfo.MaxLbaCnt = + (BlockLimits->MaximumUnmapLbaCount4 << 24) | + (BlockLimits->MaximumUnmapLbaCount3 << 16) | + (BlockLimits->MaximumUnmapLbaCount2 << 8) | + BlockLimits->MaximumUnmapLbaCount1; + ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt = + (BlockLimits->MaximumUnmapBlockDescriptorCount4 << 24) | + (BlockLimits->MaximumUnmapBlockDescriptorCount3 << 16) | + (BlockLimits->MaximumUnmapBlockDescriptorCount2 << 8) | + BlockLimits->MaximumUnmapBlockDescriptorCount1; + ScsiDiskDevice->EraseBlock.EraseLengthGranularity = + (BlockLimits->OptimalUnmapGranularity4 << 24) | + (BlockLimits->OptimalUnmapGranularity3 << 16) | + (BlockLimits->OptimalUnmapGranularity2 << 8) | + BlockLimits->OptimalUnmapGranularity1; + if (BlockLimits->UnmapGranularityAlignmentValid != 0) { + ScsiDiskDevice->UnmapInfo.GranularityAlignment = + (BlockLimits->UnmapGranularityAlignment4 << 24) | + (BlockLimits->UnmapGranularityAlignment3 << 16) | + (BlockLimits->UnmapGranularityAlignment2 << 8) | + BlockLimits->UnmapGranularityAlignment1; + } + + if (ScsiDiskDevice->EraseBlock.EraseLengthGranularity == 0) { + // + // A value of 0 indicates that the optimal unmap granularity is + // not reported. + // + ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1; + } + + ScsiDiskDevice->BlockLimitsVpdSupported = TRUE; + } + + FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE)); + } + } + + FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE)); + } + } + + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + + } else if (Status == EFI_NOT_READY) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR) + // + + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + // + // if goes here, meant ScsiInquiryCommand() failed. + // if ScsiDiskRequestSenseKeys() succeeds at last, + // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE) + // + MaxRetry = 3; + for (Index = 0; Index < MaxRetry; Index++) { + Status = ScsiDiskRequestSenseKeys ( + ScsiDiskDevice, + NeedRetry, + &SenseDataArray, + &NumberOfSenseKeys, + TRUE + ); + if (!EFI_ERROR (Status)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } + + if (!*NeedRetry) { + return EFI_DEVICE_ERROR; + } + } + // + // ScsiDiskRequestSenseKeys() failed after several rounds of retry. + // set *NeedRetry = FALSE to avoid the outside caller try again. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; +} + +/** + To test device. + + When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense; + When Test Unit Ready command encounters any error caused by host adapter or + target, return error without retrieving Sense Keys. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates try again + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The pointer of the number of sense data array + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to test unit + +**/ +EFI_STATUS +ScsiDiskTestUnitReady ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys + ) +{ + EFI_STATUS Status; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINT8 Index; + UINT8 MaxRetry; + + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + *NumberOfSenseKeys = 0; + + // + // Parameter 3 and 4: do not require sense data, retrieve it when needed. + // + Status = ScsiTestUnitReadyCommand ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus + ); + // + // no need to check HostAdapterStatus and TargetStatus + // + if (Status == EFI_NOT_READY) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR) + // + + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + if (SenseDataLength != 0) { + *NumberOfSenseKeys = SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA); + *SenseDataArray = ScsiDiskDevice->SenseData; + return EFI_SUCCESS; + } + + MaxRetry = 3; + for (Index = 0; Index < MaxRetry; Index++) { + Status = ScsiDiskRequestSenseKeys ( + ScsiDiskDevice, + NeedRetry, + SenseDataArray, + NumberOfSenseKeys, + FALSE + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + if (!*NeedRetry) { + return EFI_DEVICE_ERROR; + } + } + // + // ScsiDiskRequestSenseKeys() failed after several rounds of retry. + // set *NeedRetry = FALSE to avoid the outside caller try again. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; +} + +/** + Parsing Sense Keys which got from request sense command. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param NumberOfSenseKeys The number of sense key + @param Action The pointer of action which indicates what is need to do next + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to complete the parsing + +**/ +EFI_STATUS +DetectMediaParsingSenseKeys ( + OUT SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN NumberOfSenseKeys, + OUT UINTN *Action + ) +{ + BOOLEAN RetryLater; + + // + // Default is to read capacity, unless.. + // + *Action = ACTION_READ_CAPACITY; + + if (NumberOfSenseKeys == 0) { + if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) { + *Action = ACTION_NO_ACTION; + } + return EFI_SUCCESS; + } + + if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) { + // + // No Sense Key returned from last submitted command + // + if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) { + *Action = ACTION_NO_ACTION; + } + return EFI_SUCCESS; + } + + if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) { + ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE; + ScsiDiskDevice->BlkIo.Media->LastBlock = 0; + *Action = ACTION_NO_ACTION; + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n")); + return EFI_SUCCESS; + } + + if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) { + ScsiDiskDevice->BlkIo.Media->MediaId++; + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n")); + return EFI_SUCCESS; + } + + if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) { + *Action = ACTION_RETRY_COMMAND_LATER; + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n")); + return EFI_SUCCESS; + } + + if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) { + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n")); + *Action = ACTION_RETRY_WITH_BACKOFF_ALGO; + return EFI_DEVICE_ERROR; + } + + if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) { + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n")); + *Action = ACTION_RETRY_WITH_BACKOFF_ALGO; + return EFI_DEVICE_ERROR; + } + + if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) { + if (RetryLater) { + *Action = ACTION_RETRY_COMMAND_LATER; + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n")); + return EFI_SUCCESS; + } + *Action = ACTION_NO_ACTION; + return EFI_DEVICE_ERROR; + } + + *Action = ACTION_RETRY_WITH_BACKOFF_ALGO; + DEBUG ((EFI_D_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code)); + return EFI_SUCCESS; +} + + +/** + Send read capacity command to device and get the device parameter. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates if need a retry + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The number of sense key + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to read capacity or sense data is received. + +**/ +EFI_STATUS +ScsiDiskReadCapacity ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys + ) +{ + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + EFI_STATUS CommandStatus; + EFI_STATUS Status; + UINT8 Index; + UINT8 MaxRetry; + UINT8 SenseDataLength; + UINT32 DataLength10; + UINT32 DataLength16; + EFI_SCSI_DISK_CAPACITY_DATA *CapacityData10; + EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16; + + CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA)); + if (CapacityData10 == NULL) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + if (CapacityData16 == NULL) { + FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA)); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + SenseDataLength = 0; + DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA); + DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16); + ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA)); + ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + + *NumberOfSenseKeys = 0; + *NeedRetry = FALSE; + + // + // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh, + // 16 byte command should be used to access large hard disk >2TB + // + CommandStatus = ScsiReadCapacityCommand ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) CapacityData10, + &DataLength10, + FALSE + ); + + ScsiDiskDevice->Cdb16Byte = FALSE; + if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) && + (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff)) { + // + // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB + // + ScsiDiskDevice->Cdb16Byte = TRUE; + // + // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock + // and LowestAlignedLba + // + CommandStatus = ScsiReadCapacity16Command ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) CapacityData16, + &DataLength16, + FALSE + ); + } + + // + // no need to check HostAdapterStatus and TargetStatus + // + if (CommandStatus == EFI_SUCCESS) { + GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16); + FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA)); + FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + return EFI_SUCCESS; + } + + FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA)); + FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + + if (CommandStatus == EFI_NOT_READY) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + // + // if goes here, meant ScsiReadCapacityCommand() failed. + // if ScsiDiskRequestSenseKeys() succeeds at last, + // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE) + // + MaxRetry = 3; + for (Index = 0; Index < MaxRetry; Index++) { + + Status = ScsiDiskRequestSenseKeys ( + ScsiDiskDevice, + NeedRetry, + SenseDataArray, + NumberOfSenseKeys, + TRUE + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + if (!*NeedRetry) { + return EFI_DEVICE_ERROR; + } + } + // + // ScsiDiskRequestSenseKeys() failed after several rounds of retry. + // set *NeedRetry = FALSE to avoid the outside caller try again. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; +} + +/** + Check the HostAdapter status and re-interpret it in EFI_STATUS. + + @param HostAdapterStatus Host Adapter status + + @retval EFI_SUCCESS Host adapter is OK. + @retval EFI_TIMEOUT Timeout. + @retval EFI_NOT_READY Adapter NOT ready. + @retval EFI_DEVICE_ERROR Adapter device error. + +**/ +EFI_STATUS +CheckHostAdapterStatus ( + IN UINT8 HostAdapterStatus + ) +{ + switch (HostAdapterStatus) { + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK: + return EFI_SUCCESS; + + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND: + return EFI_TIMEOUT; + + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET: + return EFI_NOT_READY; + + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE: + case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR: + return EFI_DEVICE_ERROR; + + default: + return EFI_SUCCESS; + } +} + + +/** + Check the target status and re-interpret it in EFI_STATUS. + + @param TargetStatus Target status + + @retval EFI_NOT_READY Device is NOT ready. + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CheckTargetStatus ( + IN UINT8 TargetStatus + ) +{ + switch (TargetStatus) { + case EFI_EXT_SCSI_STATUS_TARGET_GOOD: + case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION: + case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET: + return EFI_SUCCESS; + + case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE: + case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET: + case EFI_EXT_SCSI_STATUS_TARGET_BUSY: + case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL: + return EFI_NOT_READY; + + case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT: + return EFI_DEVICE_ERROR; + + default: + return EFI_SUCCESS; + } +} + + +/** + Retrieve all sense keys from the device. + + When encountering error during the process, if retrieve sense keys before + error encountered, it returns the sense keys with return status set to EFI_SUCCESS, + and NeedRetry set to FALSE; otherwise, return the proper return status. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates if need a retry + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The number of sense key + @param AskResetIfError The flag indicates if need reset when error occurs + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to request sense key + +**/ +EFI_STATUS +ScsiDiskRequestSenseKeys ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys, + IN BOOLEAN AskResetIfError + ) +{ + EFI_SCSI_SENSE_DATA *PtrSenseData; + UINT8 SenseDataLength; + BOOLEAN SenseReq; + EFI_STATUS Status; + EFI_STATUS FallStatus; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + + FallStatus = EFI_SUCCESS; + SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA); + + ZeroMem ( + ScsiDiskDevice->SenseData, + sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber) + ); + + *NumberOfSenseKeys = 0; + *SenseDataArray = ScsiDiskDevice->SenseData; + Status = EFI_SUCCESS; + PtrSenseData = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA)); + if (PtrSenseData == NULL) { + return EFI_DEVICE_ERROR; + } + + for (SenseReq = TRUE; SenseReq;) { + ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA)); + Status = ScsiRequestSenseCommand ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + PtrSenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus + ); + if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) { + FallStatus = EFI_SUCCESS; + + } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + FallStatus = EFI_DEVICE_ERROR; + + } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + FallStatus = EFI_DEVICE_ERROR; + + } else if (Status == EFI_DEVICE_ERROR) { + if (AskResetIfError) { + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + } + + FallStatus = EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (FallStatus)) { + if (*NumberOfSenseKeys != 0) { + *NeedRetry = FALSE; + Status = EFI_SUCCESS; + goto EXIT; + } else { + Status = EFI_DEVICE_ERROR; + goto EXIT; + } + } + + CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength); + (*NumberOfSenseKeys) += 1; + + // + // no more sense key or number of sense keys exceeds predefined, + // skip the loop. + // + if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || + (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) { + SenseReq = FALSE; + } + } + +EXIT: + FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA)); + return Status; +} + + +/** + Get information from media read capacity command. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA + @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16 + +**/ +VOID +GetMediaInfo ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10, + IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16 + ) +{ + UINT8 *Ptr; + + if (!ScsiDiskDevice->Cdb16Byte) { + ScsiDiskDevice->BlkIo.Media->LastBlock = ((UINT32) Capacity10->LastLba3 << 24) | + (Capacity10->LastLba2 << 16) | + (Capacity10->LastLba1 << 8) | + Capacity10->LastLba0; + + ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) | + (Capacity10->BlockSize2 << 16) | + (Capacity10->BlockSize1 << 8) | + Capacity10->BlockSize0; + ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0; + ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 0; + if (!ScsiDiskDevice->BlockLimitsVpdSupported) { + ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) ScsiDiskDevice->BlkIo.Media->LastBlock; + } + } else { + Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock; + *Ptr++ = Capacity16->LastLba0; + *Ptr++ = Capacity16->LastLba1; + *Ptr++ = Capacity16->LastLba2; + *Ptr++ = Capacity16->LastLba3; + *Ptr++ = Capacity16->LastLba4; + *Ptr++ = Capacity16->LastLba5; + *Ptr++ = Capacity16->LastLba6; + *Ptr = Capacity16->LastLba7; + + ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) | + (Capacity16->BlockSize2 << 16) | + (Capacity16->BlockSize1 << 8) | + Capacity16->BlockSize0; + + ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) | + Capacity16->LowestAlignLogic1; + ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = (1 << Capacity16->LogicPerPhysical); + if (!ScsiDiskDevice->BlockLimitsVpdSupported) { + if (ScsiDiskDevice->BlkIo.Media->LastBlock > (UINT32) -1) { + ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) -1; + } else { + ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) ScsiDiskDevice->BlkIo.Media->LastBlock; + } + } + } + + ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE; +} + +/** + Parse Inquiry data. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + +**/ +VOID +ParseInquiryData ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice + ) +{ + ScsiDiskDevice->FixedDevice = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1); + ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice); +} + +/** + Read sector from SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Buffer The buffer to fill in the read out data + @param Lba Logic block address + @param NumberOfBlocks The number of blocks to read + + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskReadSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks + ) +{ + UINTN BlocksRemaining; + UINT8 *PtrBuffer; + UINT32 BlockSize; + UINT32 ByteCount; + UINT32 MaxBlock; + UINT32 SectorCount; + UINT32 NextSectorCount; + UINT64 Timeout; + EFI_STATUS Status; + UINT8 Index; + UINT8 MaxRetry; + BOOLEAN NeedRetry; + + Status = EFI_SUCCESS; + + BlocksRemaining = NumberOfBlocks; + BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize; + + // + // limit the data bytes that can be transferred by one Read(10) or Read(16) Command + // + if (!ScsiDiskDevice->Cdb16Byte) { + MaxBlock = 0xFFFF; + } else { + MaxBlock = 0xFFFFFFFF; + } + + PtrBuffer = Buffer; + + while (BlocksRemaining > 0) { + + if (BlocksRemaining <= MaxBlock) { + if (!ScsiDiskDevice->Cdb16Byte) { + SectorCount = (UINT16) BlocksRemaining; + } else { + SectorCount = (UINT32) BlocksRemaining; + } + } else { + SectorCount = MaxBlock; + } + + ByteCount = SectorCount * BlockSize; + // + // |------------------------|-----------------|------------------|-----------------| + // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // + // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use + // the lowest transfer rate to calculate the possible maximum timeout value for each operation. + // From the above table, we could know 2.1Mbytes per second is lowest one. + // The timeout value is rounded up to nearest integer and here an additional 30s is added + // to follow ATA spec in which it mentioned that the device may take up to 30s to respond + // commands in the Standby/Idle mode. + // + Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31); + + MaxRetry = 2; + for (Index = 0; Index < MaxRetry; Index++) { + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskRead10 ( + ScsiDiskDevice, + &NeedRetry, + Timeout, + PtrBuffer, + &ByteCount, + (UINT32) Lba, + SectorCount + ); + } else { + Status = ScsiDiskRead16 ( + ScsiDiskDevice, + &NeedRetry, + Timeout, + PtrBuffer, + &ByteCount, + Lba, + SectorCount + ); + } + if (!EFI_ERROR (Status)) { + break; + } + + if (!NeedRetry) { + return EFI_DEVICE_ERROR; + } + + // + // We need to retry. However, if ScsiDiskRead10() or ScsiDiskRead16() has + // lowered ByteCount on output, we must make sure that we lower + // SectorCount accordingly. SectorCount will be encoded in the CDB, and + // it is invalid to request more sectors in the CDB than the entire + // transfer (ie. ByteCount) can carry. + // + // In addition, ByteCount is only expected to go down, or stay unchanged. + // Therefore we don't need to update Timeout: the original timeout should + // accommodate shorter transfers too. + // + NextSectorCount = ByteCount / BlockSize; + if (NextSectorCount < SectorCount) { + SectorCount = NextSectorCount; + // + // Account for any rounding down. + // + ByteCount = SectorCount * BlockSize; + } + } + + if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) { + return EFI_DEVICE_ERROR; + } + + // + // actual transferred sectors + // + SectorCount = ByteCount / BlockSize; + + Lba += SectorCount; + PtrBuffer = PtrBuffer + SectorCount * BlockSize; + BlocksRemaining -= SectorCount; + } + + return EFI_SUCCESS; +} + +/** + Write sector to SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Buffer The buffer of data to be written into SCSI Disk + @param Lba Logic block address + @param NumberOfBlocks The number of blocks to read + + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskWriteSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks + ) +{ + UINTN BlocksRemaining; + UINT8 *PtrBuffer; + UINT32 BlockSize; + UINT32 ByteCount; + UINT32 MaxBlock; + UINT32 SectorCount; + UINT32 NextSectorCount; + UINT64 Timeout; + EFI_STATUS Status; + UINT8 Index; + UINT8 MaxRetry; + BOOLEAN NeedRetry; + + Status = EFI_SUCCESS; + + BlocksRemaining = NumberOfBlocks; + BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize; + + // + // limit the data bytes that can be transferred by one Read(10) or Read(16) Command + // + if (!ScsiDiskDevice->Cdb16Byte) { + MaxBlock = 0xFFFF; + } else { + MaxBlock = 0xFFFFFFFF; + } + + PtrBuffer = Buffer; + + while (BlocksRemaining > 0) { + + if (BlocksRemaining <= MaxBlock) { + if (!ScsiDiskDevice->Cdb16Byte) { + SectorCount = (UINT16) BlocksRemaining; + } else { + SectorCount = (UINT32) BlocksRemaining; + } + } else { + SectorCount = MaxBlock; + } + + ByteCount = SectorCount * BlockSize; + // + // |------------------------|-----------------|------------------|-----------------| + // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // + // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use + // the lowest transfer rate to calculate the possible maximum timeout value for each operation. + // From the above table, we could know 2.1Mbytes per second is lowest one. + // The timeout value is rounded up to nearest integer and here an additional 30s is added + // to follow ATA spec in which it mentioned that the device may take up to 30s to respond + // commands in the Standby/Idle mode. + // + Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31); + MaxRetry = 2; + for (Index = 0; Index < MaxRetry; Index++) { + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskWrite10 ( + ScsiDiskDevice, + &NeedRetry, + Timeout, + PtrBuffer, + &ByteCount, + (UINT32) Lba, + SectorCount + ); + } else { + Status = ScsiDiskWrite16 ( + ScsiDiskDevice, + &NeedRetry, + Timeout, + PtrBuffer, + &ByteCount, + Lba, + SectorCount + ); + } + if (!EFI_ERROR (Status)) { + break; + } + + if (!NeedRetry) { + return EFI_DEVICE_ERROR; + } + + // + // We need to retry. However, if ScsiDiskWrite10() or ScsiDiskWrite16() + // has lowered ByteCount on output, we must make sure that we lower + // SectorCount accordingly. SectorCount will be encoded in the CDB, and + // it is invalid to request more sectors in the CDB than the entire + // transfer (ie. ByteCount) can carry. + // + // In addition, ByteCount is only expected to go down, or stay unchanged. + // Therefore we don't need to update Timeout: the original timeout should + // accommodate shorter transfers too. + // + NextSectorCount = ByteCount / BlockSize; + if (NextSectorCount < SectorCount) { + SectorCount = NextSectorCount; + // + // Account for any rounding down. + // + ByteCount = SectorCount * BlockSize; + } + } + + if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) { + return EFI_DEVICE_ERROR; + } + // + // actual transferred sectors + // + SectorCount = ByteCount / BlockSize; + + Lba += SectorCount; + PtrBuffer = PtrBuffer + SectorCount * BlockSize; + BlocksRemaining -= SectorCount; + } + + return EFI_SUCCESS; +} + +/** + Asynchronously read sector from SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param Buffer The buffer to fill in the read out data. + @param Lba Logic block address. + @param NumberOfBlocks The number of blocks to read. + @param Token A pointer to the token associated with the + non-blocking read request. + + @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL. + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskAsyncReadSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + UINTN BlocksRemaining; + UINT8 *PtrBuffer; + UINT32 BlockSize; + UINT32 ByteCount; + UINT32 MaxBlock; + UINT32 SectorCount; + UINT64 Timeout; + SCSI_BLKIO2_REQUEST *BlkIo2Req; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST)); + if (BlkIo2Req == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BlkIo2Req->Token = Token; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link); + gBS->RestoreTPL (OldTpl); + + InitializeListHead (&BlkIo2Req->ScsiRWQueue); + + Status = EFI_SUCCESS; + + BlocksRemaining = NumberOfBlocks; + BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize; + + // + // Limit the data bytes that can be transferred by one Read(10) or Read(16) + // Command + // + if (!ScsiDiskDevice->Cdb16Byte) { + MaxBlock = 0xFFFF; + } else { + MaxBlock = 0xFFFFFFFF; + } + + PtrBuffer = Buffer; + + while (BlocksRemaining > 0) { + + if (BlocksRemaining <= MaxBlock) { + if (!ScsiDiskDevice->Cdb16Byte) { + SectorCount = (UINT16) BlocksRemaining; + } else { + SectorCount = (UINT32) BlocksRemaining; + } + } else { + SectorCount = MaxBlock; + } + + ByteCount = SectorCount * BlockSize; + // + // |------------------------|-----------------|------------------|-----------------| + // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // + // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, + // we have to use the lowest transfer rate to calculate the possible + // maximum timeout value for each operation. + // From the above table, we could know 2.1Mbytes per second is lowest one. + // The timeout value is rounded up to nearest integer and here an additional + // 30s is added to follow ATA spec in which it mentioned that the device + // may take up to 30s to respond commands in the Standby/Idle mode. + // + Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31); + + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncRead10 ( + ScsiDiskDevice, + Timeout, + 0, + PtrBuffer, + ByteCount, + (UINT32) Lba, + SectorCount, + BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncRead16 ( + ScsiDiskDevice, + Timeout, + 0, + PtrBuffer, + ByteCount, + Lba, + SectorCount, + BlkIo2Req, + Token + ); + } + if (EFI_ERROR (Status)) { + // + // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data + // length of a SCSI I/O command is too large. + // In this case, we retry sending the SCSI command with a data length + // half of its previous value. + // + if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) { + if ((MaxBlock > 1) && (SectorCount > 1)) { + MaxBlock = MIN (MaxBlock, SectorCount) >> 1; + continue; + } + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + // + // Free the SCSI_BLKIO2_REQUEST structure only when there is no other + // SCSI sub-task running. Otherwise, it will be freed in the callback + // function ScsiDiskNotify(). + // + RemoveEntryList (&BlkIo2Req->Link); + FreePool (BlkIo2Req); + BlkIo2Req = NULL; + gBS->RestoreTPL (OldTpl); + + // + // It is safe to return error status to the caller, since there is no + // previous SCSI sub-task executing. + // + Status = EFI_DEVICE_ERROR; + goto Done; + } else { + gBS->RestoreTPL (OldTpl); + + // + // There are previous SCSI commands still running, EFI_SUCCESS should + // be returned to make sure that the caller does not free resources + // still using by these SCSI commands. + // + Status = EFI_SUCCESS; + goto Done; + } + } + + // + // Sectors submitted for transfer + // + SectorCount = ByteCount / BlockSize; + + Lba += SectorCount; + PtrBuffer = PtrBuffer + SectorCount * BlockSize; + BlocksRemaining -= SectorCount; + } + + Status = EFI_SUCCESS; + +Done: + if (BlkIo2Req != NULL) { + BlkIo2Req->LastScsiRW = TRUE; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + RemoveEntryList (&BlkIo2Req->Link); + FreePool (BlkIo2Req); + BlkIo2Req = NULL; + + gBS->SignalEvent (Token->Event); + } + gBS->RestoreTPL (OldTpl); + } + + return Status; +} + +/** + Asynchronously write sector to SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param Buffer The buffer of data to be written into SCSI Disk. + @param Lba Logic block address. + @param NumberOfBlocks The number of blocks to read. + @param Token A pointer to the token associated with the + non-blocking read request. + + @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskAsyncWriteSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + UINTN BlocksRemaining; + UINT8 *PtrBuffer; + UINT32 BlockSize; + UINT32 ByteCount; + UINT32 MaxBlock; + UINT32 SectorCount; + UINT64 Timeout; + SCSI_BLKIO2_REQUEST *BlkIo2Req; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST)); + if (BlkIo2Req == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BlkIo2Req->Token = Token; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link); + gBS->RestoreTPL (OldTpl); + + InitializeListHead (&BlkIo2Req->ScsiRWQueue); + + Status = EFI_SUCCESS; + + BlocksRemaining = NumberOfBlocks; + BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize; + + // + // Limit the data bytes that can be transferred by one Read(10) or Read(16) + // Command + // + if (!ScsiDiskDevice->Cdb16Byte) { + MaxBlock = 0xFFFF; + } else { + MaxBlock = 0xFFFFFFFF; + } + + PtrBuffer = Buffer; + + while (BlocksRemaining > 0) { + + if (BlocksRemaining <= MaxBlock) { + if (!ScsiDiskDevice->Cdb16Byte) { + SectorCount = (UINT16) BlocksRemaining; + } else { + SectorCount = (UINT32) BlocksRemaining; + } + } else { + SectorCount = MaxBlock; + } + + ByteCount = SectorCount * BlockSize; + // + // |------------------------|-----------------|------------------|-----------------| + // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec | + // |------------------------|-----------------|------------------|-----------------| + // + // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, + // we have to use the lowest transfer rate to calculate the possible + // maximum timeout value for each operation. + // From the above table, we could know 2.1Mbytes per second is lowest one. + // The timeout value is rounded up to nearest integer and here an additional + // 30s is added to follow ATA spec in which it mentioned that the device + // may take up to 30s to respond commands in the Standby/Idle mode. + // + Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31); + + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncWrite10 ( + ScsiDiskDevice, + Timeout, + 0, + PtrBuffer, + ByteCount, + (UINT32) Lba, + SectorCount, + BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncWrite16 ( + ScsiDiskDevice, + Timeout, + 0, + PtrBuffer, + ByteCount, + Lba, + SectorCount, + BlkIo2Req, + Token + ); + } + if (EFI_ERROR (Status)) { + // + // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data + // length of a SCSI I/O command is too large. + // In this case, we retry sending the SCSI command with a data length + // half of its previous value. + // + if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) { + if ((MaxBlock > 1) && (SectorCount > 1)) { + MaxBlock = MIN (MaxBlock, SectorCount) >> 1; + continue; + } + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + // + // Free the SCSI_BLKIO2_REQUEST structure only when there is no other + // SCSI sub-task running. Otherwise, it will be freed in the callback + // function ScsiDiskNotify(). + // + RemoveEntryList (&BlkIo2Req->Link); + FreePool (BlkIo2Req); + BlkIo2Req = NULL; + gBS->RestoreTPL (OldTpl); + + // + // It is safe to return error status to the caller, since there is no + // previous SCSI sub-task executing. + // + Status = EFI_DEVICE_ERROR; + goto Done; + } else { + gBS->RestoreTPL (OldTpl); + + // + // There are previous SCSI commands still running, EFI_SUCCESS should + // be returned to make sure that the caller does not free resources + // still using by these SCSI commands. + // + Status = EFI_SUCCESS; + goto Done; + } + } + + // + // Sectors submitted for transfer + // + SectorCount = ByteCount / BlockSize; + + Lba += SectorCount; + PtrBuffer = PtrBuffer + SectorCount * BlockSize; + BlocksRemaining -= SectorCount; + } + + Status = EFI_SUCCESS; + +Done: + if (BlkIo2Req != NULL) { + BlkIo2Req->LastScsiRW = TRUE; + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) { + RemoveEntryList (&BlkIo2Req->Link); + FreePool (BlkIo2Req); + BlkIo2Req = NULL; + + gBS->SignalEvent (Token->Event); + } + gBS->RestoreTPL (OldTpl); + } + + return Status; +} + + +/** + Submit Read(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to read + + @return EFI_STATUS is returned by calling ScsiRead10Command(). +**/ +EFI_STATUS +ScsiDiskRead10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + OUT UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount + ) +{ + UINT8 SenseDataLength; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINTN Action; + + // + // Implement a backoff algorithm to resolve some compatibility issues that + // some SCSI targets or ATAPI devices couldn't correctly response reading/writing + // big data in a single operation. + // This algorithm will at first try to execute original request. If the request fails + // with media error sense data or else, it will reduce the transfer length to half and + // try again till the operation succeeds or fails with one sector transfer length. + // +BackOff: + *NeedRetry = FALSE; + Action = ACTION_NO_ACTION; + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + ReturnStatus = ScsiRead10Command ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + DataBuffer, + DataLength, + StartLba, + SectorCount + ); + + if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return ReturnStatus; + } + + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) { + DEBUG ((EFI_D_ERROR, "ScsiDiskRead10: Check Condition happened!\n")); + Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action); + if (Action == ACTION_RETRY_COMMAND_LATER) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) { + if (SectorCount <= 1) { + // + // Jump out if the operation still fails with one sector transfer length. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // Try again with half length if the sense data shows we need to retry. + // + SectorCount >>= 1; + *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize; + goto BackOff; + } else { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + Submit Write(10) Command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to write + + @return EFI_STATUS is returned by calling ScsiWrite10Command(). + +**/ +EFI_STATUS +ScsiDiskWrite10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + IN UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINTN Action; + + // + // Implement a backoff algorithm to resolve some compatibility issues that + // some SCSI targets or ATAPI devices couldn't correctly response reading/writing + // big data in a single operation. + // This algorithm will at first try to execute original request. If the request fails + // with media error sense data or else, it will reduce the transfer length to half and + // try again till the operation succeeds or fails with one sector transfer length. + // +BackOff: + *NeedRetry = FALSE; + Action = ACTION_NO_ACTION; + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + ReturnStatus = ScsiWrite10Command ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + DataBuffer, + DataLength, + StartLba, + SectorCount + ); + if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return ReturnStatus; + } + + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) { + DEBUG ((EFI_D_ERROR, "ScsiDiskWrite10: Check Condition happened!\n")); + Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action); + if (Action == ACTION_RETRY_COMMAND_LATER) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) { + if (SectorCount <= 1) { + // + // Jump out if the operation still fails with one sector transfer length. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // Try again with half length if the sense data shows we need to retry. + // + SectorCount >>= 1; + *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize; + goto BackOff; + } else { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + Submit Read(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to read + + @return EFI_STATUS is returned by calling ScsiRead16Command(). +**/ +EFI_STATUS +ScsiDiskRead16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + OUT UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount + ) +{ + UINT8 SenseDataLength; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINTN Action; + + // + // Implement a backoff algorithm to resolve some compatibility issues that + // some SCSI targets or ATAPI devices couldn't correctly response reading/writing + // big data in a single operation. + // This algorithm will at first try to execute original request. If the request fails + // with media error sense data or else, it will reduce the transfer length to half and + // try again till the operation succeeds or fails with one sector transfer length. + // +BackOff: + *NeedRetry = FALSE; + Action = ACTION_NO_ACTION; + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + ReturnStatus = ScsiRead16Command ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + DataBuffer, + DataLength, + StartLba, + SectorCount + ); + if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return ReturnStatus; + } + + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) { + DEBUG ((EFI_D_ERROR, "ScsiDiskRead16: Check Condition happened!\n")); + Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action); + if (Action == ACTION_RETRY_COMMAND_LATER) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) { + if (SectorCount <= 1) { + // + // Jump out if the operation still fails with one sector transfer length. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // Try again with half length if the sense data shows we need to retry. + // + SectorCount >>= 1; + *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize; + goto BackOff; + } else { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + Submit Write(16) Command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to write + + @return EFI_STATUS is returned by calling ScsiWrite16Command(). + +**/ +EFI_STATUS +ScsiDiskWrite16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + IN UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount + ) +{ + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINTN Action; + + // + // Implement a backoff algorithm to resolve some compatibility issues that + // some SCSI targets or ATAPI devices couldn't correctly response reading/writing + // big data in a single operation. + // This algorithm will at first try to execute original request. If the request fails + // with media error sense data or else, it will reduce the transfer length to half and + // try again till the operation succeeds or fails with one sector transfer length. + // +BackOff: + *NeedRetry = FALSE; + Action = ACTION_NO_ACTION; + SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA)); + ReturnStatus = ScsiWrite16Command ( + ScsiDiskDevice->ScsiIo, + Timeout, + ScsiDiskDevice->SenseData, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + DataBuffer, + DataLength, + StartLba, + SectorCount + ); + if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) { + *NeedRetry = FALSE; + return ReturnStatus; + } + + // + // go ahead to check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + Status = CheckHostAdapterStatus (HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + Status = CheckTargetStatus (TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Status == EFI_DEVICE_ERROR) { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + + if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) { + DEBUG ((EFI_D_ERROR, "ScsiDiskWrite16: Check Condition happened!\n")); + Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action); + if (Action == ACTION_RETRY_COMMAND_LATER) { + *NeedRetry = TRUE; + return EFI_DEVICE_ERROR; + } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) { + if (SectorCount <= 1) { + // + // Jump out if the operation still fails with one sector transfer length. + // + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + // + // Try again with half length if the sense data shows we need to retry. + // + SectorCount >>= 1; + *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize; + goto BackOff; + } else { + *NeedRetry = FALSE; + return EFI_DEVICE_ERROR; + } + } + + return ReturnStatus; +} + + +/** + Internal helper notify function in which determine whether retry of a SCSI + Read/Write command is needed and signal the event passed from Block I/O(2) if + the SCSI I/O operation completes. + + @param Event The instance of EFI_EVENT. + @param Context The parameter passed in. + +**/ +VOID +EFIAPI +ScsiDiskNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + SCSI_ASYNC_RW_REQUEST *Request; + SCSI_DISK_DEV *ScsiDiskDevice; + EFI_BLOCK_IO2_TOKEN *Token; + UINTN Action; + UINT32 OldDataLength; + UINT32 OldSectorCount; + UINT8 MaxRetry; + + gBS->CloseEvent (Event); + + Request = (SCSI_ASYNC_RW_REQUEST *) Context; + ScsiDiskDevice = Request->ScsiDiskDevice; + Token = Request->BlkIo2Req->Token; + OldDataLength = Request->DataLength; + OldSectorCount = Request->SectorCount; + MaxRetry = 2; + + // + // If previous sub-tasks already fails, no need to process this sub-task. + // + if (Token->TransactionStatus != EFI_SUCCESS) { + goto Exit; + } + + // + // Check HostAdapterStatus and TargetStatus + // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL) + // + Status = CheckHostAdapterStatus (Request->HostAdapterStatus); + if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) { + if (++Request->TimesRetry > MaxRetry) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } else { + goto Retry; + } + } else if (Status == EFI_DEVICE_ERROR) { + // + // reset the scsi channel + // + ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo); + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + + Status = CheckTargetStatus (Request->TargetStatus); + if (Status == EFI_NOT_READY) { + // + // reset the scsi device + // + ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo); + if (++Request->TimesRetry > MaxRetry) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } else { + goto Retry; + } + } else if (Status == EFI_DEVICE_ERROR) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) { + DEBUG ((EFI_D_ERROR, "ScsiDiskNotify: Check Condition happened!\n")); + + Status = DetectMediaParsingSenseKeys ( + ScsiDiskDevice, + Request->SenseData, + Request->SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), + &Action + ); + if (Action == ACTION_RETRY_COMMAND_LATER) { + if (++Request->TimesRetry > MaxRetry) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } else { + goto Retry; + } + } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) { + if (Request->SectorCount <= 1) { + // + // Jump out if the operation still fails with one sector transfer + // length. + // + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + // + // Try again with two half length request if the sense data shows we need + // to retry. + // + Request->SectorCount >>= 1; + Request->DataLength = Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize; + Request->TimesRetry = 0; + + goto Retry; + } else { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + } + + // + // This sub-task succeeds, no need to retry. + // + goto Exit; + +Retry: + if (Request->InBuffer != NULL) { + // + // SCSI read command + // + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncRead10 ( + ScsiDiskDevice, + Request->Timeout, + Request->TimesRetry, + Request->InBuffer, + Request->DataLength, + (UINT32) Request->StartLba, + Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncRead16 ( + ScsiDiskDevice, + Request->Timeout, + Request->TimesRetry, + Request->InBuffer, + Request->DataLength, + Request->StartLba, + Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } + + if (EFI_ERROR (Status)) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } else if (OldSectorCount != Request->SectorCount) { + // + // Original sub-task will be split into two new sub-tasks with smaller + // DataLength + // + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncRead10 ( + ScsiDiskDevice, + Request->Timeout, + 0, + Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize, + OldDataLength - Request->DataLength, + (UINT32) Request->StartLba + Request->SectorCount, + OldSectorCount - Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncRead16 ( + ScsiDiskDevice, + Request->Timeout, + 0, + Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize, + OldDataLength - Request->DataLength, + Request->StartLba + Request->SectorCount, + OldSectorCount - Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } + if (EFI_ERROR (Status)) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + } + } else { + // + // SCSI write command + // + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncWrite10 ( + ScsiDiskDevice, + Request->Timeout, + Request->TimesRetry, + Request->OutBuffer, + Request->DataLength, + (UINT32) Request->StartLba, + Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncWrite16 ( + ScsiDiskDevice, + Request->Timeout, + Request->TimesRetry, + Request->OutBuffer, + Request->DataLength, + Request->StartLba, + Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } + + if (EFI_ERROR (Status)) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } else if (OldSectorCount != Request->SectorCount) { + // + // Original sub-task will be split into two new sub-tasks with smaller + // DataLength + // + if (!ScsiDiskDevice->Cdb16Byte) { + Status = ScsiDiskAsyncWrite10 ( + ScsiDiskDevice, + Request->Timeout, + 0, + Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize, + OldDataLength - Request->DataLength, + (UINT32) Request->StartLba + Request->SectorCount, + OldSectorCount - Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } else { + Status = ScsiDiskAsyncWrite16 ( + ScsiDiskDevice, + Request->Timeout, + 0, + Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize, + OldDataLength - Request->DataLength, + Request->StartLba + Request->SectorCount, + OldSectorCount - Request->SectorCount, + Request->BlkIo2Req, + Token + ); + } + if (EFI_ERROR (Status)) { + Token->TransactionStatus = EFI_DEVICE_ERROR; + goto Exit; + } + } + } + +Exit: + RemoveEntryList (&Request->Link); + if ((IsListEmpty (&Request->BlkIo2Req->ScsiRWQueue)) && + (Request->BlkIo2Req->LastScsiRW)) { + // + // The last SCSI R/W command of a BlockIo2 request completes + // + RemoveEntryList (&Request->BlkIo2Req->Link); + FreePool (Request->BlkIo2Req); // Should be freed only once + gBS->SignalEvent (Token->Event); + } + + FreePool (Request->SenseData); + FreePool (Request); +} + + +/** + Submit Async Read(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer to fill with the read out data. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to read. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiRead10CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncRead10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + OUT UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + SCSI_ASYNC_RW_REQUEST *Request; + EFI_EVENT AsyncIoEvent; + EFI_TPL OldTpl; + + AsyncIoEvent = NULL; + + Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST)); + if (Request == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link); + gBS->RestoreTPL (OldTpl); + + Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA)); + Request->SenseData = AllocateZeroPool (Request->SenseDataLength); + if (Request->SenseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + Request->ScsiDiskDevice = ScsiDiskDevice; + Request->Timeout = Timeout; + Request->TimesRetry = TimesRetry; + Request->InBuffer = DataBuffer; + Request->DataLength = DataLength; + Request->StartLba = StartLba; + Request->SectorCount = SectorCount; + Request->BlkIo2Req = BlkIo2Req; + + // + // Create Event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ScsiDiskNotify, + Request, + &AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + Status = ScsiRead10CommandEx ( + ScsiDiskDevice->ScsiIo, + Request->Timeout, + Request->SenseData, + &Request->SenseDataLength, + &Request->HostAdapterStatus, + &Request->TargetStatus, + Request->InBuffer, + &Request->DataLength, + (UINT32) Request->StartLba, + Request->SectorCount, + AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + return EFI_SUCCESS; + +ErrorExit: + if (AsyncIoEvent != NULL) { + gBS->CloseEvent (AsyncIoEvent); + } + + if (Request != NULL) { + if (Request->SenseData != NULL) { + FreePool (Request->SenseData); + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&Request->Link); + gBS->RestoreTPL (OldTpl); + + FreePool (Request); + } + + return Status; +} + + +/** + Submit Async Write(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer contains the data to write. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to write. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiWrite10CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncWrite10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + IN UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + SCSI_ASYNC_RW_REQUEST *Request; + EFI_EVENT AsyncIoEvent; + EFI_TPL OldTpl; + + AsyncIoEvent = NULL; + + Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST)); + if (Request == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link); + gBS->RestoreTPL (OldTpl); + + Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA)); + Request->SenseData = AllocateZeroPool (Request->SenseDataLength); + if (Request->SenseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + Request->ScsiDiskDevice = ScsiDiskDevice; + Request->Timeout = Timeout; + Request->TimesRetry = TimesRetry; + Request->OutBuffer = DataBuffer; + Request->DataLength = DataLength; + Request->StartLba = StartLba; + Request->SectorCount = SectorCount; + Request->BlkIo2Req = BlkIo2Req; + + // + // Create Event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ScsiDiskNotify, + Request, + &AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + Status = ScsiWrite10CommandEx ( + ScsiDiskDevice->ScsiIo, + Request->Timeout, + Request->SenseData, + &Request->SenseDataLength, + &Request->HostAdapterStatus, + &Request->TargetStatus, + Request->OutBuffer, + &Request->DataLength, + (UINT32) Request->StartLba, + Request->SectorCount, + AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + return EFI_SUCCESS; + +ErrorExit: + if (AsyncIoEvent != NULL) { + gBS->CloseEvent (AsyncIoEvent); + } + + if (Request != NULL) { + if (Request->SenseData != NULL) { + FreePool (Request->SenseData); + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&Request->Link); + gBS->RestoreTPL (OldTpl); + + FreePool (Request); + } + + return Status; +} + + +/** + Submit Async Read(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer to fill with the read out data. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to read. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiRead16CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncRead16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + OUT UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + SCSI_ASYNC_RW_REQUEST *Request; + EFI_EVENT AsyncIoEvent; + EFI_TPL OldTpl; + + AsyncIoEvent = NULL; + + Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST)); + if (Request == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link); + gBS->RestoreTPL (OldTpl); + + Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA)); + Request->SenseData = AllocateZeroPool (Request->SenseDataLength); + if (Request->SenseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + Request->ScsiDiskDevice = ScsiDiskDevice; + Request->Timeout = Timeout; + Request->TimesRetry = TimesRetry; + Request->InBuffer = DataBuffer; + Request->DataLength = DataLength; + Request->StartLba = StartLba; + Request->SectorCount = SectorCount; + Request->BlkIo2Req = BlkIo2Req; + + // + // Create Event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ScsiDiskNotify, + Request, + &AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + Status = ScsiRead16CommandEx ( + ScsiDiskDevice->ScsiIo, + Request->Timeout, + Request->SenseData, + &Request->SenseDataLength, + &Request->HostAdapterStatus, + &Request->TargetStatus, + Request->InBuffer, + &Request->DataLength, + Request->StartLba, + Request->SectorCount, + AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + return EFI_SUCCESS; + +ErrorExit: + if (AsyncIoEvent != NULL) { + gBS->CloseEvent (AsyncIoEvent); + } + + if (Request != NULL) { + if (Request->SenseData != NULL) { + FreePool (Request->SenseData); + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&Request->Link); + gBS->RestoreTPL (OldTpl); + + FreePool (Request); + } + + return Status; +} + + +/** + Submit Async Write(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer contains the data to write. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to write. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiWrite16CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncWrite16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + IN UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + SCSI_ASYNC_RW_REQUEST *Request; + EFI_EVENT AsyncIoEvent; + EFI_TPL OldTpl; + + AsyncIoEvent = NULL; + + Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST)); + if (Request == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link); + gBS->RestoreTPL (OldTpl); + + Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA)); + Request->SenseData = AllocateZeroPool (Request->SenseDataLength); + if (Request->SenseData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ErrorExit; + } + + Request->ScsiDiskDevice = ScsiDiskDevice; + Request->Timeout = Timeout; + Request->TimesRetry = TimesRetry; + Request->OutBuffer = DataBuffer; + Request->DataLength = DataLength; + Request->StartLba = StartLba; + Request->SectorCount = SectorCount; + Request->BlkIo2Req = BlkIo2Req; + + // + // Create Event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ScsiDiskNotify, + Request, + &AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + Status = ScsiWrite16CommandEx ( + ScsiDiskDevice->ScsiIo, + Request->Timeout, + Request->SenseData, + &Request->SenseDataLength, + &Request->HostAdapterStatus, + &Request->TargetStatus, + Request->OutBuffer, + &Request->DataLength, + Request->StartLba, + Request->SectorCount, + AsyncIoEvent + ); + if (EFI_ERROR(Status)) { + goto ErrorExit; + } + + return EFI_SUCCESS; + +ErrorExit: + if (AsyncIoEvent != NULL) { + gBS->CloseEvent (AsyncIoEvent); + } + + if (Request != NULL) { + if (Request->SenseData != NULL) { + FreePool (Request->SenseData); + } + + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&Request->Link); + gBS->RestoreTPL (OldTpl); + + FreePool (Request); + } + + return Status; +} + + +/** + Check sense key to find if media presents. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE NOT any media + @retval FALSE Media presents +**/ +BOOLEAN +ScsiDiskIsNoMedia ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsNoMedia; + + IsNoMedia = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + // + // Sense Key is EFI_SCSI_SK_NOT_READY (0x2), + // Additional Sense Code is ASC_NO_MEDIA (0x3A) + // + if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) { + IsNoMedia = TRUE; + } + SensePtr++; + } + + return IsNoMedia; +} + + +/** + Parse sense key. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Error + @retval FALSE NOT error + +**/ +BOOLEAN +ScsiDiskIsMediaError ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsError; + + IsError = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + switch (SensePtr->Sense_Key) { + + case EFI_SCSI_SK_MEDIUM_ERROR: + // + // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3) + // + switch (SensePtr->Addnl_Sense_Code) { + + // + // fall through + // + case EFI_SCSI_ASC_MEDIA_ERR1: + + // + // fall through + // + case EFI_SCSI_ASC_MEDIA_ERR2: + + // + // fall through + // + case EFI_SCSI_ASC_MEDIA_ERR3: + case EFI_SCSI_ASC_MEDIA_ERR4: + IsError = TRUE; + break; + + default: + break; + } + + break; + + case EFI_SCSI_SK_NOT_READY: + // + // Sense Key is EFI_SCSI_SK_NOT_READY (0x2) + // + switch (SensePtr->Addnl_Sense_Code) { + // + // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6) + // + case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN: + IsError = TRUE; + break; + + default: + break; + } + break; + + default: + break; + } + + SensePtr++; + } + + return IsError; +} + + +/** + Check sense key to find if hardware error happens. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Hardware error exits. + @retval FALSE NO error. + +**/ +BOOLEAN +ScsiDiskIsHardwareError ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsError; + + IsError = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + // + // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4) + // + if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) { + IsError = TRUE; + } + + SensePtr++; + } + + return IsError; +} + + +/** + Check sense key to find if media has changed. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Media is changed. + @retval FALSE Media is NOT changed. +**/ +BOOLEAN +ScsiDiskIsMediaChange ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsMediaChanged; + + IsMediaChanged = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + // + // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6), + // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28) + // + if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) { + IsMediaChanged = TRUE; + } + + SensePtr++; + } + + return IsMediaChanged; +} + +/** + Check sense key to find if reset happens. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE It is reset before. + @retval FALSE It is NOT reset before. + +**/ +BOOLEAN +ScsiDiskIsResetBefore ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsResetBefore; + + IsResetBefore = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + // + // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6) + // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29) + // + if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) { + IsResetBefore = TRUE; + } + + SensePtr++; + } + + return IsResetBefore; +} + +/** + Check sense key to find if the drive is ready. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + @param RetryLater The flag means if need a retry + + @retval TRUE Drive is ready. + @retval FALSE Drive is NOT ready. + +**/ +BOOLEAN +ScsiDiskIsDriveReady ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts, + OUT BOOLEAN *RetryLater + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN IsReady; + + IsReady = TRUE; + *RetryLater = FALSE; + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + switch (SensePtr->Sense_Key) { + + case EFI_SCSI_SK_NOT_READY: + // + // Sense Key is EFI_SCSI_SK_NOT_READY (0x2) + // + switch (SensePtr->Addnl_Sense_Code) { + case EFI_SCSI_ASC_NOT_READY: + // + // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4) + // + switch (SensePtr->Addnl_Sense_Code_Qualifier) { + case EFI_SCSI_ASCQ_IN_PROGRESS: + // + // Additional Sense Code Qualifier is + // EFI_SCSI_ASCQ_IN_PROGRESS (0x1) + // + IsReady = FALSE; + *RetryLater = TRUE; + break; + + default: + IsReady = FALSE; + *RetryLater = FALSE; + break; + } + break; + + default: + break; + } + break; + + default: + break; + } + + SensePtr++; + } + + return IsReady; +} + +/** + Check sense key to find if it has sense key. + + @param SenseData - The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts - The number of sense key + + @retval TRUE It has sense key. + @retval FALSE It has NOT any sense key. + +**/ +BOOLEAN +ScsiDiskHaveSenseKey ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ) +{ + EFI_SCSI_SENSE_DATA *SensePtr; + UINTN Index; + BOOLEAN HaveSenseKey; + + if (SenseCounts == 0) { + HaveSenseKey = FALSE; + } else { + HaveSenseKey = TRUE; + } + + SensePtr = SenseData; + + for (Index = 0; Index < SenseCounts; Index++) { + + // + // Sense Key is SK_NO_SENSE (0x0) + // + if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) && + (Index == 0)) { + HaveSenseKey = FALSE; + } + + SensePtr++; + } + + return HaveSenseKey; +} + +/** + Release resource about disk device. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + +**/ +VOID +ReleaseScsiDiskDeviceResources ( + IN SCSI_DISK_DEV *ScsiDiskDevice + ) +{ + if (ScsiDiskDevice == NULL) { + return ; + } + + if (ScsiDiskDevice->SenseData != NULL) { + FreePool (ScsiDiskDevice->SenseData); + ScsiDiskDevice->SenseData = NULL; + } + + if (ScsiDiskDevice->ControllerNameTable != NULL) { + FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable); + ScsiDiskDevice->ControllerNameTable = NULL; + } + + FreePool (ScsiDiskDevice); + + ScsiDiskDevice = NULL; +} + +/** + Determine if Block Io & Block Io2 should be produced. + + + @param ChildHandle Child Handle to retrieve Parent information. + + @retval TRUE Should produce Block Io & Block Io2. + @retval FALSE Should not produce Block Io & Block Io2. + +**/ +BOOLEAN +DetermineInstallBlockIo ( + IN EFI_HANDLE ChildHandle + ) +{ + EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; + + // + // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence, + // check its attribute, logic or physical. + // + ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle); + if (ExtScsiPassThru != NULL) { + if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) { + return TRUE; + } + } + + // + // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence, + // check its attribute, logic or physical. + // + ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle); + if (ScsiPassThru != NULL) { + if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + Search protocol database and check to see if the protocol + specified by ProtocolGuid is present on a ControllerHandle and opened by + ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + If the ControllerHandle is found, then the protocol specified by ProtocolGuid + will be opened on it. + + + @param ProtocolGuid ProtocolGuid pointer. + @param ChildHandle Child Handle to retrieve Parent information. + +**/ +VOID * +EFIAPI +GetParentProtocol ( + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE ChildHandle + ) +{ + UINTN Index; + UINTN HandleCount; + VOID *Interface; + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + + // + // Retrieve the list of all handles from the handle database + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + ProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle + // + for (Index = 0; Index < HandleCount; Index++) { + Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid); + if (!EFI_ERROR (Status)) { + Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface); + if (!EFI_ERROR (Status)) { + gBS->FreePool (HandleBuffer); + return Interface; + } + } + } + + gBS->FreePool (HandleBuffer); + return NULL; +} + +/** + Determine if EFI Erase Block Protocol should be produced. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Handle of device. + + @retval TRUE Should produce EFI Erase Block Protocol. + @retval FALSE Should not produce EFI Erase Block Protocol. + +**/ +BOOLEAN +DetermineInstallEraseBlock ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ) +{ + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + EFI_STATUS CommandStatus; + EFI_STATUS Status; + BOOLEAN UfsDevice; + BOOLEAN RetVal; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + UINT8 SenseDataLength; + UINT32 DataLength16; + EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16; + + UfsDevice = FALSE; + RetVal = TRUE; + CapacityData16 = NULL; + + // + // UNMAP command is not supported by any of the UFS WLUNs. + // + if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) { + RetVal = FALSE; + goto Done; + } + + Status = gBS->HandleProtocol ( + ChildHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePathNode + ); + // + // Device Path protocol must be installed on the device handle. + // + ASSERT_EFI_ERROR (Status); + + while (!IsDevicePathEndType (DevicePathNode)) { + // + // For now, only support Erase Block Protocol on UFS devices. + // + if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) && + (DevicePathNode->SubType == MSG_UFS_DP)) { + UfsDevice = TRUE; + break; + } + + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + if (!UfsDevice) { + RetVal = FALSE; + goto Done; + } + + // + // Check whether the erase functionality is enabled on the UFS device. + // + CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + if (CapacityData16 == NULL) { + RetVal = FALSE; + goto Done; + } + + SenseDataLength = 0; + DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16); + ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + + CommandStatus = ScsiReadCapacity16Command ( + ScsiDiskDevice->ScsiIo, + SCSI_DISK_TIMEOUT, + NULL, + &SenseDataLength, + &HostAdapterStatus, + &TargetStatus, + (VOID *) CapacityData16, + &DataLength16, + FALSE + ); + + if (CommandStatus == EFI_SUCCESS) { + // + // Universal Flash Storage (UFS) Version 2.0 + // Section 11.3.9.2 + // Bits TPE and TPRZ should both be set to enable the erase feature on UFS. + // + if (((CapacityData16->LowestAlignLogic2 & BIT7) == 0) || + ((CapacityData16->LowestAlignLogic2 & BIT6) == 0)) { + DEBUG (( + EFI_D_VERBOSE, + "ScsiDisk EraseBlock: Either TPE or TPRZ is not set: 0x%x.\n", + CapacityData16->LowestAlignLogic2 + )); + + RetVal = FALSE; + goto Done; + } + } else { + DEBUG (( + EFI_D_VERBOSE, + "ScsiDisk EraseBlock: ReadCapacity16 failed with status %r.\n", + CommandStatus + )); + + RetVal = FALSE; + goto Done; + } + + // + // Check whether the UFS device server implements the UNMAP command. + // + if ((ScsiDiskDevice->UnmapInfo.MaxLbaCnt == 0) || + (ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt == 0)) { + DEBUG (( + EFI_D_VERBOSE, + "ScsiDisk EraseBlock: The device server does not implement the UNMAP command.\n" + )); + + RetVal = FALSE; + goto Done; + } + +Done: + if (CapacityData16 != NULL) { + FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16)); + } + + return RetVal; +} + +/** + Determine if EFI Storage Security Command Protocol should be produced. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Handle of device. + + @retval TRUE Should produce EFI Storage Security Command Protocol. + @retval FALSE Should not produce EFI Storage Security Command Protocol. + +**/ +BOOLEAN +DetermineInstallStorageSecurity ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + UFS_DEVICE_PATH *UfsDevice; + BOOLEAN RetVal; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + + UfsDevice = NULL; + RetVal = TRUE; + + Status = gBS->HandleProtocol ( + ChildHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePathNode + ); + // + // Device Path protocol must be installed on the device handle. + // + ASSERT_EFI_ERROR (Status); + + while (!IsDevicePathEndType (DevicePathNode)) { + // + // For now, only support Storage Security Command Protocol on UFS devices. + // + if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) && + (DevicePathNode->SubType == MSG_UFS_DP)) { + UfsDevice = (UFS_DEVICE_PATH *) DevicePathNode; + break; + } + + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + if (UfsDevice == NULL) { + RetVal = FALSE; + goto Done; + } + + if (UfsDevice->Lun != UFS_WLUN_RPMB) { + RetVal = FALSE; + } + +Done: + return RetVal; +} + +/** + Provides inquiry information for the controller type. + + This function is used by the IDE bus driver to get inquiry data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in, out] InquiryData Pointer to a buffer for the inquiry data. + @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class + @retval EFI_DEVICE_ERROR Error reading InquiryData from device + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ) +{ + EFI_STATUS Status; + SCSI_DISK_DEV *ScsiDiskDevice; + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This); + + Status = EFI_BUFFER_TOO_SMALL; + if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) { + Status = EFI_SUCCESS; + CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData)); + } + *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData); + return Status; +} + + +/** + Provides identify information for the controller type. + + This function is used by the IDE bus driver to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in, out] IdentifyData Pointer to a buffer for the identify data. + @param[in, out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ) +{ + EFI_STATUS Status; + SCSI_DISK_DEV *ScsiDiskDevice; + + if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) { + // + // Physical SCSI bus does not support this data class. + // + return EFI_NOT_FOUND; + } + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This); + + Status = EFI_BUFFER_TOO_SMALL; + if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) { + Status = EFI_SUCCESS; + CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData)); + } + *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData); + return Status; +} + +/** + Provides sense data information for the controller type. + + This function is used by the IDE bus driver to get sense data. + Data format of Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in, out] SenseData Pointer to the SenseData. + @param[in, out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ) +{ + return EFI_NOT_FOUND; +} + + +/** + This function is used by the IDE bus driver to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ) +{ + SCSI_DISK_DEV *ScsiDiskDevice; + + if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) { + // + // This is not an IDE physical device. + // + return EFI_UNSUPPORTED; + } + + ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This); + *IdeChannel = ScsiDiskDevice->Channel; + *IdeDevice = ScsiDiskDevice->Device; + + return EFI_SUCCESS; +} + + +/** + Issues ATA IDENTIFY DEVICE command to identify ATAPI device. + + This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to + implement Identify() interface for DiskInfo protocol. The ATA command is sent + via SCSI Request Packet. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + + @retval EFI_SUCCESS The ATAPI device identify data were retrieved successfully. + @retval others Some error occurred during the identification that ATAPI device. + +**/ +EFI_STATUS +AtapiIdentifyDevice ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice + ) +{ + EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket; + UINT8 Cdb[6]; + + // + // Initialize SCSI REQUEST_PACKET and 6-byte Cdb + // + ZeroMem (&CommandPacket, sizeof (CommandPacket)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = ATA_CMD_IDENTIFY_DEVICE; + CommandPacket.Timeout = SCSI_DISK_TIMEOUT; + CommandPacket.Cdb = Cdb; + CommandPacket.CdbLength = (UINT8) sizeof (Cdb); + CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData; + CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData); + + return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL); +} + + +/** + Initialize the installation of DiskInfo protocol. + + This function prepares for the installation of DiskInfo protocol on the child handle. + By default, it installs DiskInfo protocol with SCSI interface GUID. If it further + detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID + to be IDE/AHCI interface GUID. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Child handle to install DiskInfo protocol. + +**/ +VOID +InitializeInstallDiskInfo ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *ChildDevicePathNode; + ATAPI_DEVICE_PATH *AtapiDevicePath; + SATA_DEVICE_PATH *SataDevicePath; + UINTN IdentifyRetry; + + Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode); + // + // Device Path protocol must be installed on the device handle. + // + ASSERT_EFI_ERROR (Status); + // + // Copy the DiskInfo protocol template. + // + CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate)); + + while (!IsDevicePathEnd (DevicePathNode)) { + ChildDevicePathNode = NextDevicePathNode (DevicePathNode); + if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) && + (DevicePathSubType (DevicePathNode) == HW_PCI_DP) && + (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) || + (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) { + + IdentifyRetry = 3; + do { + // + // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol + // with IDE/AHCI interface GUID. + // + Status = AtapiIdentifyDevice (ScsiDiskDevice); + if (!EFI_ERROR (Status)) { + if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) { + // + // We find the valid ATAPI device path + // + AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode; + ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary; + ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster; + // + // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device. + // + CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid); + } else { + // + // We find the valid SATA device path + // + SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode; + ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber; + ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber; + // + // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device. + // + CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid); + } + return; + } + } while (--IdentifyRetry > 0); + } else if ((DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType (ChildDevicePathNode) == MSG_UFS_DP)) { + CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoUfsInterfaceGuid); + break; + } + DevicePathNode = ChildDevicePathNode; + } + + return; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.h b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.h new file mode 100644 index 000000000..ed9bbd6f8 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.h @@ -0,0 +1,1600 @@ +/** @file + Header file for SCSI Disk Driver. + +Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SCSI_DISK_H_ +#define _SCSI_DISK_H_ + + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define IS_DEVICE_FIXED(a) (a)->FixedDevice ? 1 : 0 + +#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0) + +#define UFS_WLUN_RPMB 0xC4 + +typedef struct { + UINT32 MaxLbaCnt; + UINT32 MaxBlkDespCnt; + UINT32 GranularityAlignment; +} SCSI_UNMAP_PARAM_INFO; + +#define SCSI_DISK_DEV_SIGNATURE SIGNATURE_32 ('s', 'c', 'd', 'k') + +typedef struct { + UINT32 Signature; + + EFI_HANDLE Handle; + + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL StorageSecurity; + + EFI_BLOCK_IO_PROTOCOL BlkIo; + EFI_BLOCK_IO2_PROTOCOL BlkIo2; + EFI_BLOCK_IO_MEDIA BlkIoMedia; + EFI_ERASE_BLOCK_PROTOCOL EraseBlock; + EFI_SCSI_IO_PROTOCOL *ScsiIo; + UINT8 DeviceType; + BOOLEAN FixedDevice; + UINT16 Reserved; + + EFI_SCSI_SENSE_DATA *SenseData; + UINTN SenseDataNumber; + EFI_SCSI_INQUIRY_DATA InquiryData; + + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + EFI_DISK_INFO_PROTOCOL DiskInfo; + + // + // The following fields are only valid for ATAPI/SATA device + // + UINT32 Channel; + UINT32 Device; + ATAPI_IDENTIFY_DATA IdentifyData; + + // + // Scsi UNMAP command parameters information + // + SCSI_UNMAP_PARAM_INFO UnmapInfo; + BOOLEAN BlockLimitsVpdSupported; + + // + // The flag indicates if 16-byte command can be used + // + BOOLEAN Cdb16Byte; + + // + // The queue for asynchronous task requests + // + LIST_ENTRY AsyncTaskQueue; +} SCSI_DISK_DEV; + +#define SCSI_DISK_DEV_FROM_BLKIO(a) CR (a, SCSI_DISK_DEV, BlkIo, SCSI_DISK_DEV_SIGNATURE) +#define SCSI_DISK_DEV_FROM_BLKIO2(a) CR (a, SCSI_DISK_DEV, BlkIo2, SCSI_DISK_DEV_SIGNATURE) +#define SCSI_DISK_DEV_FROM_ERASEBLK(a) CR (a, SCSI_DISK_DEV, EraseBlock, SCSI_DISK_DEV_SIGNATURE) +#define SCSI_DISK_DEV_FROM_STORSEC(a) CR (a, SCSI_DISK_DEV, StorageSecurity, SCSI_DISK_DEV_SIGNATURE) + +#define SCSI_DISK_DEV_FROM_DISKINFO(a) CR (a, SCSI_DISK_DEV, DiskInfo, SCSI_DISK_DEV_SIGNATURE) + +// +// Asynchronous I/O request +// +// +// Private data structure for a BlockIo2 request +// +typedef struct { + EFI_BLOCK_IO2_TOKEN *Token; + // + // The flag indicates if the last Scsi Read/Write sub-task for a BlockIo2 + // request is sent to device + // + BOOLEAN LastScsiRW; + + // + // The queue for Scsi Read/Write sub-tasks of a BlockIo2 request + // + LIST_ENTRY ScsiRWQueue; + + LIST_ENTRY Link; +} SCSI_BLKIO2_REQUEST; + +// +// Private data structure for a SCSI Read/Write request +// +typedef struct { + SCSI_DISK_DEV *ScsiDiskDevice; + UINT64 Timeout; + EFI_SCSI_SENSE_DATA *SenseData; + UINT8 SenseDataLength; + UINT8 HostAdapterStatus; + UINT8 TargetStatus; + UINT8 *InBuffer; + UINT8 *OutBuffer; + UINT32 DataLength; + UINT64 StartLba; + UINT32 SectorCount; + UINT8 TimesRetry; + + // + // The BlockIo2 request this SCSI command belongs to + // + SCSI_BLKIO2_REQUEST *BlkIo2Req; + + LIST_ENTRY Link; +} SCSI_ASYNC_RW_REQUEST; + +// +// Private data structure for an EraseBlock request +// +typedef struct { + EFI_ERASE_BLOCK_TOKEN *Token; + + EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket; + + LIST_ENTRY Link; +} SCSI_ERASEBLK_REQUEST; + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gScsiDiskComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gScsiDiskComponentName2; +// +// action code used in detect media process +// +#define ACTION_NO_ACTION 0x00 +#define ACTION_READ_CAPACITY 0x01 +#define ACTION_RETRY_COMMAND_LATER 0x02 +#define ACTION_RETRY_WITH_BACKOFF_ALGO 0x03 + +#define SCSI_COMMAND_VERSION_1 0x01 +#define SCSI_COMMAND_VERSION_2 0x02 +#define SCSI_COMMAND_VERSION_3 0x03 + +// +// SCSI Disk Timeout Experience Value +// +// As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, the timeout +// value is updated to 30s to follow ATA/ATAPI spec in which the device may take up to 30s +// to respond command. +// +#define SCSI_DISK_TIMEOUT EFI_TIMER_PERIOD_SECONDS (30) + +/** + Test to see if this driver supports ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. + If any other agent wishes to call Supported() it must also follow these + calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + This service is called by the EFI boot service ConnectController(). In order + to make drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these calling restrictions. If + any other agent wishes to call Start() it must also follow these calling + restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + This service is called by the EFI boot service DisconnectController(). + In order to make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() must follow these + calling restrictions. If any other agent wishes to call Stop() it must + also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Reset SCSI Disk. + + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + @param ExtendedVerification The flag about if extend verificate + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice(). + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + + +/** + The function is to Read Block from SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected + @param Lba The logic block address + @param BufferSize The size of Buffer + @param Buffer The buffer to fill the read out data + + @retval EFI_SUCCESS Successfully to read out block. + @retval EFI_DEVICE_ERROR Fail to detect media. + @retval EFI_NO_MEDIA Media is not present. + @retval EFI_MEDIA_CHANGED Media has changed. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER Invalid parameter passed in. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + + +/** + The function is to Write Block to SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + @param MediaId The Id of Media detected + @param Lba The logic block address + @param BufferSize The size of Buffer + @param Buffer The buffer to fill the read out data + + @retval EFI_SUCCESS Successfully to read out block. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR Fail to detect media. + @retval EFI_NO_MEDIA Media is not present. + @retval EFI_MEDIA_CHANGED Media has changed. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER Invalid parameter passed in. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + + +/** + Flush Block to Disk. + + EFI_SUCCESS is returned directly. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL + + @retval EFI_SUCCESS All outstanding data was written to the device + +**/ +EFI_STATUS +EFIAPI +ScsiDiskFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + + +/** + Reset SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO2_PROTOCOL. + @param ExtendedVerification The flag about if extend verificate. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice(). + +**/ +EFI_STATUS +EFIAPI +ScsiDiskResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + The function is to Read Block from SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected. + @param Lba The logic block address. + @param Token A pointer to the token associated with the transaction. + @param BufferSize The size of Buffer. + @param Buffer The buffer to fill the read out data. + + @retval EFI_SUCCESS The read request was queued if Token-> Event is + not NULL. The data was read correctly from the + device if theToken-> Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + The function is to Write Block to SCSI Disk. + + @param This The pointer of EFI_BLOCK_IO_PROTOCOL. + @param MediaId The Id of Media detected. + @param Lba The logic block address. + @param Token A pointer to the token associated with the transaction. + @param BufferSize The size of Buffer. + @param Buffer The buffer to fill the read out data. + + @retval EFI_SUCCESS The data were written correctly to the device. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the write operation. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not + valid, or the buffer is not on proper + alignment. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + @param Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to + write data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId OPTIONAL, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ); + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT if the + time required to execute the receive data command is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskSendData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId OPTIONAL, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer + ); + + +/** + Provides inquiry information for the controller type. + + This function is used by the IDE bus driver to get inquiry data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in, out] InquiryData Pointer to a buffer for the inquiry data. + @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class + @retval EFI_DEVICE_ERROR Error reading InquiryData from device + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ); + + +/** + Provides identify information for the controller type. + + This function is used by the IDE bus driver to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in, out] IdentifyData Pointer to a buffer for the identify data. + @param[in, out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ); + + +/** + Provides sense data information for the controller type. + + This function is used by the IDE bus driver to get sense data. + Data format of Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in, out] SenseData Pointer to the SenseData. + @param[in, out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ); + +/** + This function is used by the IDE bus driver to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +ScsiDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ); + + +/** + Detect Device and read out capacity ,if error occurs, parse the sense key. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param MustReadCapacity The flag about reading device capacity + @param MediaChange The pointer of flag indicates if media has changed + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to detect media + +**/ +EFI_STATUS +ScsiDiskDetectMedia ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN BOOLEAN MustReadCapacity, + OUT BOOLEAN *MediaChange + ); + +/** + To test device. + + When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense; + When Test Unit Ready command encounters any error caused by host adapter or + target, return error without retrieving Sense Keys. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates try again + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The pointer of the number of sense data array + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to test unit + +**/ +EFI_STATUS +ScsiDiskTestUnitReady ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys + ); + + +/** + Parsing Sense Keys which got from request sense command. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param NumberOfSenseKeys The number of sense key + @param Action The pointer of action which indicates what is need to do next + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to complete the parsing + +**/ +EFI_STATUS +DetectMediaParsingSenseKeys ( + OUT SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN NumberOfSenseKeys, + OUT UINTN *Action + ); + + +/** + Send read capacity command to device and get the device parameter. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates if need a retry + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The number of sense key + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to read capacity + +**/ +EFI_STATUS +ScsiDiskReadCapacity ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys + ); + +/** + Check the HostAdapter status and re-interpret it in EFI_STATUS. + + @param HostAdapterStatus Host Adapter status + + @retval EFI_SUCCESS Host adapter is OK. + @retval EFI_TIMEOUT Timeout. + @retval EFI_NOT_READY Adapter NOT ready. + @retval EFI_DEVICE_ERROR Adapter device error. + +**/ +EFI_STATUS +CheckHostAdapterStatus ( + IN UINT8 HostAdapterStatus + ); + + +/** + Check the target status and re-interpret it in EFI_STATUS. + + @param TargetStatus Target status + + @retval EFI_NOT_READY Device is NOT ready. + @retval EFI_DEVICE_ERROR + @retval EFI_SUCCESS + +**/ +EFI_STATUS +CheckTargetStatus ( + IN UINT8 TargetStatus + ); + +/** + Retrieve all sense keys from the device. + + When encountering error during the process, if retrieve sense keys before + error encountered, it returns the sense keys with return status set to EFI_SUCCESS, + and NeedRetry set to FALSE; otherwise, return the proper return status. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry The pointer of flag indicates if need a retry + @param SenseDataArray The pointer of an array of sense data + @param NumberOfSenseKeys The number of sense key + @param AskResetIfError The flag indicates if need reset when error occurs + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to request sense key + +**/ +EFI_STATUS +ScsiDiskRequestSenseKeys ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + OUT EFI_SCSI_SENSE_DATA **SenseDataArray, + OUT UINTN *NumberOfSenseKeys, + IN BOOLEAN AskResetIfError + ); + +/** + Send out Inquiry command to Device. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param NeedRetry Indicates if needs try again when error happens + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to detect media + +**/ +EFI_STATUS +ScsiDiskInquiryDevice ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry + ); + +/** + Parse Inquiry data. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + +**/ +VOID +ParseInquiryData ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice + ); + +/** + Read sector from SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Buffer The buffer to fill in the read out data + @param Lba Logic block address + @param NumberOfBlocks The number of blocks to read + + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskReadSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks + ); + +/** + Write sector to SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Buffer The buffer of data to be written into SCSI Disk + @param Lba Logic block address + @param NumberOfBlocks The number of blocks to read + + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskWriteSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks + ); + +/** + Asynchronously read sector from SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param Buffer The buffer to fill in the read out data. + @param Lba Logic block address. + @param NumberOfBlocks The number of blocks to read. + @param Token A pointer to the token associated with the + non-blocking read request. + + @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL. + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskAsyncReadSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Asynchronously write sector to SCSI Disk. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param Buffer The buffer of data to be written into SCSI Disk. + @param Lba Logic block address. + @param NumberOfBlocks The number of blocks to read. + @param Token A pointer to the token associated with the + non-blocking read request. + + @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL + @retval EFI_DEVICE_ERROR Indicates a device error. + @retval EFI_SUCCESS Operation is successful. + +**/ +EFI_STATUS +ScsiDiskAsyncWriteSectors ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN VOID *Buffer, + IN EFI_LBA Lba, + IN UINTN NumberOfBlocks, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Submit Read(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to read + + @return EFI_STATUS is returned by calling ScsiRead10Command(). +**/ +EFI_STATUS +ScsiDiskRead10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + OUT UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount + ); + +/** + Submit Write(10) Command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to write + + @return EFI_STATUS is returned by calling ScsiWrite10Command(). + +**/ +EFI_STATUS +ScsiDiskWrite10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + IN UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount + ); + +/** + Submit Read(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to read + + @return EFI_STATUS is returned by calling ScsiRead16Command(). +**/ +EFI_STATUS +ScsiDiskRead16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + OUT UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount + ); + +/** + Submit Write(16) Command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice + @param NeedRetry The pointer of flag indicates if needs retry if error happens + @param Timeout The time to complete the command + @param DataBuffer The buffer to fill with the read out data + @param DataLength The length of buffer + @param StartLba The start logic block address + @param SectorCount The number of blocks to write + + @return EFI_STATUS is returned by calling ScsiWrite16Command(). + +**/ +EFI_STATUS +ScsiDiskWrite16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + OUT BOOLEAN *NeedRetry, + IN UINT64 Timeout, + IN UINT8 *DataBuffer, + IN OUT UINT32 *DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount + ); + +/** + Submit Async Read(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer to fill with the read out data. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to read. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiRead10CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncRead10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + OUT UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Submit Async Write(10) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer contains the data to write. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to write. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiWrite10CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncWrite10 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + IN UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT32 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Submit Async Read(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer to fill with the read out data. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to read. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiRead16CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncRead16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + OUT UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Submit Async Write(16) command. + + @param ScsiDiskDevice The pointer of ScsiDiskDevice. + @param Timeout The time to complete the command. + @param TimesRetry The number of times the command has been retried. + @param DataBuffer The buffer contains the data to write. + @param DataLength The length of buffer. + @param StartLba The start logic block address. + @param SectorCount The number of blocks to write. + @param BlkIo2Req The upstream BlockIo2 request. + @param Token The pointer to the token associated with the + non-blocking read request. + + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @return others Status returned by calling + ScsiWrite16CommandEx(). + +**/ +EFI_STATUS +ScsiDiskAsyncWrite16 ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN UINT64 Timeout, + IN UINT8 TimesRetry, + IN UINT8 *DataBuffer, + IN UINT32 DataLength, + IN UINT64 StartLba, + IN UINT32 SectorCount, + IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req, + IN EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Get information from media read capacity command. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA + @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16 +**/ +VOID +GetMediaInfo ( + IN OUT SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10, + IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16 + ); + +/** + Check sense key to find if media presents. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE NOT any media + @retval FALSE Media presents +**/ +BOOLEAN +ScsiDiskIsNoMedia ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Parse sense key. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Error + @retval FALSE NOT error + +**/ +BOOLEAN +ScsiDiskIsMediaError ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check sense key to find if hardware error happens. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Hardware error exits. + @retval FALSE NO error. + +**/ +BOOLEAN +ScsiDiskIsHardwareError ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check sense key to find if media has changed. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE Media is changed. + @retval FALSE Media is NOT changed. +**/ +BOOLEAN +ScsiDiskIsMediaChange ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check sense key to find if reset happens. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + + @retval TRUE It is reset before. + @retval FALSE It is NOT reset before. + +**/ +BOOLEAN +ScsiDiskIsResetBefore ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Check sense key to find if the drive is ready. + + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts The number of sense key + @param RetryLater The flag means if need a retry + + @retval TRUE Drive is ready. + @retval FALSE Drive is NOT ready. + +**/ +BOOLEAN +ScsiDiskIsDriveReady ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts, + OUT BOOLEAN *RetryLater + ); + +/** + Check sense key to find if it has sense key. + + @param SenseData - The pointer of EFI_SCSI_SENSE_DATA + @param SenseCounts - The number of sense key + + @retval TRUE It has sense key. + @retval FALSE It has NOT any sense key. + +**/ +BOOLEAN +ScsiDiskHaveSenseKey ( + IN EFI_SCSI_SENSE_DATA *SenseData, + IN UINTN SenseCounts + ); + +/** + Release resource about disk device. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV + +**/ +VOID +ReleaseScsiDiskDeviceResources ( + IN SCSI_DISK_DEV *ScsiDiskDevice + ); + +/** + Determine if Block Io should be produced. + + + @param ChildHandle Child Handle to retrieve Parent information. + + @retval TRUE Should produce Block Io. + @retval FALSE Should not produce Block Io. + +**/ +BOOLEAN +DetermineInstallBlockIo ( + IN EFI_HANDLE ChildHandle + ); + +/** + Initialize the installation of DiskInfo protocol. + + This function prepares for the installation of DiskInfo protocol on the child handle. + By default, it installs DiskInfo protocol with SCSI interface GUID. If it further + detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID + to be IDE/AHCI interface GUID. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Child handle to install DiskInfo protocol. + +**/ +VOID +InitializeInstallDiskInfo ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ); + +/** + Search protocol database and check to see if the protocol + specified by ProtocolGuid is present on a ControllerHandle and opened by + ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + If the ControllerHandle is found, then the protocol specified by ProtocolGuid + will be opened on it. + + + @param ProtocolGuid ProtocolGuid pointer. + @param ChildHandle Child Handle to retrieve Parent information. + +**/ +VOID * +EFIAPI +GetParentProtocol ( + IN EFI_GUID *ProtocolGuid, + IN EFI_HANDLE ChildHandle + ); + +/** + Determine if EFI Erase Block Protocol should be produced. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Handle of device. + + @retval TRUE Should produce EFI Erase Block Protocol. + @retval FALSE Should not produce EFI Erase Block Protocol. + +**/ +BOOLEAN +DetermineInstallEraseBlock ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ); + +/** + Determine if EFI Storage Security Command Protocol should be produced. + + @param ScsiDiskDevice The pointer of SCSI_DISK_DEV. + @param ChildHandle Handle of device. + + @retval TRUE Should produce EFI Storage Security Command Protocol. + @retval FALSE Should not produce EFI Storage Security Command Protocol. + +**/ +BOOLEAN +DetermineInstallStorageSecurity ( + IN SCSI_DISK_DEV *ScsiDiskDevice, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.uni b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.uni new file mode 100644 index 000000000..957513064 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.uni @@ -0,0 +1,16 @@ +// /** @file +// The Scsi Disk driver is used to retrieve the media info in the attached SCSI disk. +// +// It detects the SCSI disk media and installs Block I/O Protocol on the device handle. +// +// Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Used to retrieve the media information in the attached SCSI disk" + +#string STR_MODULE_DESCRIPTION #language en-US "It detects the SCSI disk media and installs Block I/O Protocol on the device handle." + diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf new file mode 100644 index 000000000..40818e669 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf @@ -0,0 +1,71 @@ +## @file +# The Scsi Disk driver is used to retrieve the media info in the attached SCSI disk. +# It detects the SCSI disk media and installs Block I/O and Block I/O2 Protocol on +# the device handle. +# +# Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ScsiDisk + MODULE_UNI_FILE = ScsiDisk.uni + FILE_GUID = 0A66E322-3740-4cce-AD62-BD172CECCA35 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeScsiDisk + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gScsiDiskDriverBinding +# COMPONENT_NAME = gScsiDiskComponentName +# COMPONENT_NAME2 = gScsiDiskComponentName2 +# + +[Sources] + ComponentName.c + ScsiDisk.c + ScsiDisk.h + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + UefiBootServicesTableLib + UefiScsiLib + BaseMemoryLib + MemoryAllocationLib + UefiLib + UefiDriverEntryPoint + DebugLib + DevicePathLib + +[Protocols] + gEfiDiskInfoProtocolGuid ## BY_START + gEfiBlockIoProtocolGuid ## BY_START + gEfiBlockIo2ProtocolGuid ## BY_START + gEfiEraseBlockProtocolGuid ## BY_START + gEfiStorageSecurityCommandProtocolGuid ## BY_START + gEfiScsiIoProtocolGuid ## TO_START + gEfiScsiPassThruProtocolGuid ## TO_START + gEfiExtScsiPassThruProtocolGuid ## TO_START + +[Guids] + gEfiDiskInfoScsiInterfaceGuid ## SOMETIMES_PRODUCES ## UNDEFINED + gEfiDiskInfoIdeInterfaceGuid ## SOMETIMES_PRODUCES ## UNDEFINED + gEfiDiskInfoAhciInterfaceGuid ## SOMETIMES_PRODUCES ## UNDEFINED + gEfiDiskInfoUfsInterfaceGuid ## SOMETIMES_PRODUCES ## UNDEFINED + +# [Event] +# EVENT_TYPE_RELATIVE_TIMER ## CONSUMES +# + +[UserExtensions.TianoCore."ExtraFiles"] + ScsiDiskExtra.uni diff --git a/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskExtra.uni b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskExtra.uni new file mode 100644 index 000000000..b82516247 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// ScsiDisk Localized Strings and Content +// +// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SCSI Disk DXE Driver" + + -- cgit