X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FBus%2FScsi%2FScsiDiskDxe%2FScsiDisk.c;h=98e84b4ea8aae04ff02f004cc08155975c83aa2b;hp=d2a751a8ed5d316b33790d275e5fe1aada36e9db;hb=HEAD;hpb=e72a3b3ea82c6520fc195ebc36e198a9ca9ca457
diff --git a/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c b/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
index d2a751a8ed..fbc236cb46 100644
--- a/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
+++ b/MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
@@ -1,22 +1,15 @@
/** @file
SCSI disk driver that layers on every SCSI IO protocol in the system.
-Copyright (c) 2006 - 2008, Intel Corporation.
-All rights reserved. This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+Copyright (c) 1985 - 2022, American Megatrends International LLC.
+SPDX-License-Identifier: BSD-2-Clause-Patent
**/
-
#include "ScsiDisk.h"
-
-EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
+EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
ScsiDiskDriverBindingSupported,
ScsiDiskDriverBindingStart,
ScsiDiskDriverBindingStop,
@@ -25,26 +18,103 @@ EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
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));
+ }
+}
+
+/**
+ Remove trailing spaces from the string.
+
+ @param String The ASCII string to remove the trailing spaces.
+
+ @retval the new length of the string.
+**/
+UINTN
+RemoveTrailingSpaces (
+ IN OUT CHAR8 *String
+ )
+{
+ UINTN Length;
+
+ Length = AsciiStrLen (String);
+ if (Length == 0) {
+ return 0;
+ }
+
+ while ((Length > 0) && (String[Length-1] == ' ')) {
+ Length--;
+ }
+
+ String[Length] = '\0';
+ return Length;
+}
+
/**
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 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
+InitializeScsiDisk (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
)
{
- EFI_STATUS Status;
+ EFI_STATUS Status;
//
// Install driver model protocol(s).
@@ -59,7 +129,6 @@ InitializeScsiDisk(
);
ASSERT_EFI_ERROR (Status);
-
return Status;
}
@@ -97,7 +166,7 @@ ScsiDiskDriverBindingSupported (
Status = gBS->OpenProtocol (
Controller,
&gEfiScsiIoProtocolGuid,
- (VOID **) &ScsiIo,
+ (VOID **)&ScsiIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
@@ -108,7 +177,10 @@ ScsiDiskDriverBindingSupported (
Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);
if (!EFI_ERROR (Status)) {
- if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {
+ if ((DeviceType == EFI_SCSI_TYPE_DISK) ||
+ (DeviceType == EFI_SCSI_TYPE_CDROM) ||
+ (DeviceType == EFI_SCSI_TYPE_WLUN))
+ {
Status = EFI_SUCCESS;
} else {
Status = EFI_UNSUPPORTED;
@@ -124,7 +196,6 @@ ScsiDiskDriverBindingSupported (
return Status;
}
-
/**
Start this driver on ControllerHandle.
@@ -159,77 +230,93 @@ ScsiDiskDriverBindingStart (
UINT8 Index;
UINT8 MaxRetry;
BOOLEAN NeedRetry;
+ BOOLEAN MustReadCapacity;
+ CHAR8 VendorStr[VENDOR_IDENTIFICATION_LENGTH + 1];
+ CHAR8 ProductStr[PRODUCT_IDENTIFICATION_LENGTH + 1];
+ CHAR16 DeviceStr[VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2];
- Status = gBS->AllocatePool (
- EfiBootServicesData,
- sizeof (SCSI_DISK_DEV),
- (VOID **) &ScsiDiskDevice
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
+ MustReadCapacity = TRUE;
- ZeroMem (ScsiDiskDevice, sizeof (SCSI_DISK_DEV));
+ ScsiDiskDevice = (SCSI_DISK_DEV *)AllocateZeroPool (sizeof (SCSI_DISK_DEV));
+ if (ScsiDiskDevice == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
Status = gBS->OpenProtocol (
Controller,
&gEfiScsiIoProtocolGuid,
- (VOID **) &ScsiIo,
+ (VOID **)&ScsiIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
- gBS->FreePool (ScsiDiskDevice);
+ FreePool (ScsiDiskDevice);
return Status;
}
- ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE;
- ScsiDiskDevice->ScsiIo = ScsiIo;
- ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia;
- ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset;
- ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;
- ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;
- ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;
- ScsiDiskDevice->Handle = Controller;
+ 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;
- break;
+ 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_CDROM:
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
- break;
+ case EFI_SCSI_TYPE_WLUN:
+ MustReadCapacity = FALSE;
+ break;
}
+
//
// The Sense Data Array's initial size is 6
//
ScsiDiskDevice->SenseDataNumber = 6;
- Status = gBS->AllocatePool (
- EfiBootServicesData,
- sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber,
- (VOID **) &(ScsiDiskDevice->SenseData)
- );
- if (EFI_ERROR (Status)) {
+ 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
- );
- gBS->FreePool (ScsiDiskDevice);
- return Status;
+ Controller,
+ &gEfiScsiIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ FreePool (ScsiDiskDevice);
+ return EFI_OUT_OF_RESOURCES;
}
- ZeroMem (
- ScsiDiskDevice->SenseData,
- sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber
- );
-
//
- // Retrive device information
+ // Retrieve device information
//
MaxRetry = 2;
for (Index = 0; Index < MaxRetry; Index++) {
@@ -239,65 +326,114 @@ ScsiDiskDriverBindingStart (
}
if (!NeedRetry) {
- gBS->FreePool (ScsiDiskDevice->SenseData);
+ FreePool (ScsiDiskDevice->SenseData);
gBS->CloseProtocol (
Controller,
&gEfiScsiIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
- gBS->FreePool (ScsiDiskDevice);
+ FreePool (ScsiDiskDevice);
return EFI_DEVICE_ERROR;
}
}
+
//
// The second parameter "TRUE" means must
// retrieve media capacity
//
- Status = ScsiDiskDetectMedia (ScsiDiskDevice, TRUE, &Temp);
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);
if (!EFI_ERROR (Status)) {
- Status = gBS->InstallMultipleProtocolInterfaces (
- &Controller,
- &gEfiBlockIoProtocolGuid,
- &ScsiDiskDevice->BlkIo,
- NULL
- );
- }
-
- if (EFI_ERROR (Status)) {
- gBS->FreePool (ScsiDiskDevice->SenseData);
- gBS->CloseProtocol (
- Controller,
- &gEfiScsiIoProtocolGuid,
- This->DriverBindingHandle,
- Controller
- );
- gBS->FreePool (ScsiDiskDevice);
- return 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));
+ }
+ }
- 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
- );
+ 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));
+ }
+ }
+ CopyMem (
+ VendorStr,
+ &ScsiDiskDevice->InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET],
+ VENDOR_IDENTIFICATION_LENGTH
+ );
+ VendorStr[VENDOR_IDENTIFICATION_LENGTH] = 0;
+ RemoveTrailingSpaces (VendorStr);
- return EFI_SUCCESS;
+ CopyMem (
+ ProductStr,
+ &ScsiDiskDevice->InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET],
+ PRODUCT_IDENTIFICATION_LENGTH
+ );
+ ProductStr[PRODUCT_IDENTIFICATION_LENGTH] = 0;
+ RemoveTrailingSpaces (ProductStr);
+
+ UnicodeSPrint (DeviceStr, sizeof (DeviceStr), L"%a %a", VendorStr, ProductStr);
+
+ ScsiDiskDevice->ControllerNameTable = NULL;
+ AddUnicodeString2 (
+ "eng",
+ gScsiDiskComponentName.SupportedLanguages,
+ &ScsiDiskDevice->ControllerNameTable,
+ DeviceStr,
+ TRUE
+ );
+ AddUnicodeString2 (
+ "en",
+ gScsiDiskComponentName2.SupportedLanguages,
+ &ScsiDiskDevice->ControllerNameTable,
+ DeviceStr,
+ 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.
@@ -306,7 +442,7 @@ ScsiDiskDriverBindingStart (
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
@@ -320,20 +456,21 @@ ScsiDiskDriverBindingStart (
EFI_STATUS
EFIAPI
ScsiDiskDriverBindingStop (
- IN EFI_DRIVER_BINDING_PROTOCOL *This,
- IN EFI_HANDLE Controller,
- IN UINTN NumberOfChildren,
- IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
- EFI_BLOCK_IO_PROTOCOL *BlkIo;
- SCSI_DISK_DEV *ScsiDiskDevice;
- EFI_STATUS Status;
+ EFI_BLOCK_IO_PROTOCOL *BlkIo;
+ EFI_ERASE_BLOCK_PROTOCOL *EraseBlock;
+ SCSI_DISK_DEV *ScsiDiskDevice;
+ EFI_STATUS Status;
Status = gBS->OpenProtocol (
Controller,
&gEfiBlockIoProtocolGuid,
- (VOID **) &BlkIo,
+ (VOID **)&BlkIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
@@ -342,11 +479,46 @@ ScsiDiskDriverBindingStop (
return Status;
}
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);
- Status = gBS->UninstallProtocolInterface (
+ 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
+ &ScsiDiskDevice->BlkIo,
+ &gEfiBlockIo2ProtocolGuid,
+ &ScsiDiskDevice->BlkIo2,
+ &gEfiDiskInfoProtocolGuid,
+ &ScsiDiskDevice->DiskInfo,
+ NULL
);
if (!EFI_ERROR (Status)) {
gBS->CloseProtocol (
@@ -360,6 +532,7 @@ ScsiDiskDriverBindingStop (
return EFI_SUCCESS;
}
+
//
// errors met
//
@@ -376,25 +549,34 @@ ScsiDiskDriverBindingStop (
@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 retured from EFI_SCSI_IO_PROTOCOL.ResetDevice().
+ @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().
**/
EFI_STATUS
EFIAPI
ScsiDiskReset (
- IN EFI_BLOCK_IO_PROTOCOL *This,
- IN BOOLEAN ExtendedVerification
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
)
{
- EFI_TPL OldTpl;
- SCSI_DISK_DEV *ScsiDiskDevice;
- EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ SCSI_DISK_DEV *ScsiDiskDevice;
+ EFI_STATUS Status;
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);
+
+ Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
- 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;
@@ -402,6 +584,11 @@ ScsiDiskReset (
Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
Done:
gBS->RestoreTPL (OldTpl);
return Status;
@@ -427,11 +614,11 @@ Done:
EFI_STATUS
EFIAPI
ScsiDiskReadBlocks (
- IN EFI_BLOCK_IO_PROTOCOL *This,
- IN UINT32 MediaId,
- IN EFI_LBA Lba,
- IN UINTN BufferSize,
- OUT VOID *Buffer
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
)
{
SCSI_DISK_DEV *ScsiDiskDevice;
@@ -442,21 +629,12 @@ ScsiDiskReadBlocks (
BOOLEAN MediaChange;
EFI_TPL OldTpl;
- MediaChange = FALSE;
- if (Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (BufferSize == 0) {
- return EFI_SUCCESS;
- }
-
- OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
-
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);
-
- if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
+ 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;
@@ -465,20 +643,56 @@ ScsiDiskReadBlocks (
if (MediaChange) {
gBS->ReinstallProtocolInterface (
- ScsiDiskDevice->Handle,
- &gEfiBlockIoProtocolGuid,
- &ScsiDiskDevice->BlkIo,
- &ScsiDiskDevice->BlkIo
- );
+ 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
//
- Media = ScsiDiskDevice->BlkIo.Media;
- BlockSize = Media->BlockSize;
+ BlockSize = Media->BlockSize;
+
+ if (BlockSize == 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
- NumberOfBlocks = BufferSize / BlockSize;
+ NumberOfBlocks = BufferSize / BlockSize;
if (!(Media->MediaPresent)) {
Status = EFI_NO_MEDIA;
@@ -490,6 +704,16 @@ ScsiDiskReadBlocks (
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;
@@ -505,7 +729,7 @@ ScsiDiskReadBlocks (
goto Done;
}
- if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
+ if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
@@ -534,7 +758,7 @@ Done:
@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_CHNAGED Media has changed.
+ @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.
@@ -542,11 +766,11 @@ Done:
EFI_STATUS
EFIAPI
ScsiDiskWriteBlocks (
- IN EFI_BLOCK_IO_PROTOCOL *This,
- IN UINT32 MediaId,
- IN EFI_LBA Lba,
- IN UINTN BufferSize,
- IN VOID *Buffer
+ IN EFI_BLOCK_IO_PROTOCOL *This,
+ IN UINT32 MediaId,
+ IN EFI_LBA Lba,
+ IN UINTN BufferSize,
+ IN VOID *Buffer
)
{
SCSI_DISK_DEV *ScsiDiskDevice;
@@ -557,21 +781,12 @@ ScsiDiskWriteBlocks (
BOOLEAN MediaChange;
EFI_TPL OldTpl;
- MediaChange = FALSE;
- if (Buffer == NULL) {
- return EFI_INVALID_PARAMETER;
- }
-
- if (BufferSize == 0) {
- return EFI_SUCCESS;
- }
-
- OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
-
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);
-
- if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
+ 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;
@@ -580,20 +795,56 @@ ScsiDiskWriteBlocks (
if (MediaChange) {
gBS->ReinstallProtocolInterface (
- ScsiDiskDevice->Handle,
- &gEfiBlockIoProtocolGuid,
- &ScsiDiskDevice->BlkIo,
- &ScsiDiskDevice->BlkIo
- );
+ 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
//
- Media = ScsiDiskDevice->BlkIo.Media;
- BlockSize = Media->BlockSize;
+ BlockSize = Media->BlockSize;
+
+ if (BlockSize == 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
- NumberOfBlocks = BufferSize / BlockSize;
+ NumberOfBlocks = BufferSize / BlockSize;
if (!(Media->MediaPresent)) {
Status = EFI_NO_MEDIA;
@@ -605,6 +856,21 @@ ScsiDiskWriteBlocks (
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;
@@ -620,10 +886,11 @@ ScsiDiskWriteBlocks (
goto Done;
}
- if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
+ 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.
@@ -648,7 +915,7 @@ Done:
EFI_STATUS
EFIAPI
ScsiDiskFlushBlocks (
- IN EFI_BLOCK_IO_PROTOCOL *This
+ IN EFI_BLOCK_IO_PROTOCOL *This
)
{
//
@@ -657,262 +924,3729 @@ ScsiDiskFlushBlocks (
return EFI_SUCCESS;
}
-
/**
- Dectect Device and read out capacity ,if error occurs, parse the sense key.
+ Reset SCSI Disk.
- @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
+ @param This The pointer of EFI_BLOCK_IO2_PROTOCOL.
+ @param ExtendedVerification The flag about if extend verificate.
- @retval EFI_DEVICE_ERROR Indicates that error occurs
- @retval EFI_SUCCESS Successfully to detect media
+ @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
-ScsiDiskDetectMedia (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- IN BOOLEAN MustReadCapacity,
- OUT BOOLEAN *MediaChange
+EFIAPI
+ScsiDiskResetEx (
+ IN EFI_BLOCK_IO2_PROTOCOL *This,
+ IN BOOLEAN ExtendedVerification
)
{
- EFI_STATUS Status;
- EFI_STATUS ReadCapacityStatus;
- EFI_SCSI_SENSE_DATA *SenseData;
- UINTN NumberOfSenseKeys;
- BOOLEAN NeedRetry;
- BOOLEAN NeedReadCapacity;
- UINT8 Index;
- UINT8 MaxRetry;
- EFI_BLOCK_IO_MEDIA OldMedia;
- UINTN Action;
-
- Status = EFI_SUCCESS;
- ReadCapacityStatus = EFI_SUCCESS;
- SenseData = NULL;
- NumberOfSenseKeys = 0;
- NeedReadCapacity = FALSE;
- CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));
- *MediaChange = FALSE;
- MaxRetry = 3;
+ EFI_TPL OldTpl;
+ SCSI_DISK_DEV *ScsiDiskDevice;
+ EFI_STATUS Status;
- for (Index = 0; Index < MaxRetry; Index++) {
- Status = ScsiDiskTestUnitReady (
- ScsiDiskDevice,
- &NeedRetry,
- &SenseData,
- &NumberOfSenseKeys
- );
- if (!EFI_ERROR (Status)) {
- break;
- }
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
- if (!NeedRetry) {
- return Status;
+ 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 ((Index == MaxRetry) && EFI_ERROR (Status)) {
- return EFI_DEVICE_ERROR;
+ if (!ExtendedVerification) {
+ goto Done;
}
- Status = DetectMediaParsingSenseKeys (
- ScsiDiskDevice,
- SenseData,
- NumberOfSenseKeys,
- &Action
- );
+ Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
+
if (EFI_ERROR (Status)) {
- return Status;
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
}
- //
- // ACTION_NO_ACTION: need not read capacity
+
+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 ((
+ DEBUG_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 ((
+ DEBUG_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 ((
+ DEBUG_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 ((
+ DEBUG_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_NO_ACTION) {
- NeedReadCapacity = FALSE;
- } else {
- NeedReadCapacity = TRUE;
+ 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 ((
+ DEBUG_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 ((
+ DEBUG_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 ((
+ DEBUG_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 ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));
+ return EFI_SUCCESS;
+ }
+
+ if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
+ ScsiDiskDevice->BlkIo.Media->MediaId++;
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));
+ return EFI_SUCCESS;
+ }
+
+ if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {
+ *Action = ACTION_RETRY_COMMAND_LATER;
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));
+ return EFI_SUCCESS;
+ }
+
+ if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
+ DEBUG ((DEBUG_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 ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));
+ return EFI_SUCCESS;
+ }
+
+ *Action = ACTION_NO_ACTION;
+ return EFI_DEVICE_ERROR;
+ }
+
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;
+ DEBUG ((DEBUG_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;
}
//
- // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
- // retrieve capacity via Read Capacity command
+ // go ahead to check HostAdapterStatus and TargetStatus
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
//
- if (NeedReadCapacity || MustReadCapacity) {
+ Status = CheckHostAdapterStatus (HostAdapterStatus);
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
+ *NeedRetry = TRUE;
+ return EFI_DEVICE_ERROR;
+ } else if (Status == EFI_DEVICE_ERROR) {
//
- // retrieve media information
+ // reset the scsi channel
//
- MaxRetry = 3;
- for (Index = 0; Index < MaxRetry; Index++) {
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
+ *NeedRetry = FALSE;
+ return EFI_DEVICE_ERROR;
+ }
- ReadCapacityStatus = ScsiDiskReadCapacity (
- ScsiDiskDevice,
- &NeedRetry,
- &SenseData,
- &NumberOfSenseKeys
- );
- if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {
+ 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 ((DEBUG_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));
+ 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;
}
+
//
- // analyze sense key to action
- //
- Status = DetectMediaParsingSenseKeys (
- ScsiDiskDevice,
- SenseData,
- NumberOfSenseKeys,
- &Action
- );
- //
- // if Status is error, it may indicate crisis error,
- // so return without retry.
+ // Try again with half length if the sense data shows we need to retry.
//
- if (EFI_ERROR (Status)) {
- return Status;
- }
+ SectorCount >>= 1;
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
+ goto BackOff;
+ } else {
+ *NeedRetry = FALSE;
+ return EFI_DEVICE_ERROR;
+ }
+ }
- switch (Action) {
- case ACTION_NO_ACTION:
- //
- // no retry
- //
- Index = MaxRetry;
- break;
+ return ReturnStatus;
+}
- case ACTION_RETRY_COMMAND_LATER:
- //
- // retry the ReadCapacity later and continuously, until the condition
- // no longer emerges.
- // stall time is 100000us, or say 0.1 second.
- //
- gBS->Stall (100000);
- Index = 0;
- break;
+/**
+ Submit Write(10) Command.
- default:
+ @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 ((DEBUG_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));
+ 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) {
//
- // other cases, just retry the command
+ // Jump out if the operation still fails with one sector transfer length.
//
- break;
+ *NeedRetry = FALSE;
+ return EFI_DEVICE_ERROR;
}
- }
- if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {
+ //
+ // 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;
}
}
- if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {
- //
- // Media change information got from the device
- //
- *MediaChange = TRUE;
- }
+ return ReturnStatus;
+}
- if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {
- *MediaChange = TRUE;
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;
+/**
+ 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;
}
- if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {
- *MediaChange = TRUE;
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;
+ //
+ // 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;
}
- if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {
- *MediaChange = TRUE;
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;
+ 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 (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {
- if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
+ DEBUG ((DEBUG_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));
+ 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;
+ }
+
//
- // when change from no media to media present, reset the MediaId to 1.
+ // Try again with half length if the sense data shows we need to retry.
//
- ScsiDiskDevice->BlkIo.Media->MediaId = 1;
+ SectorCount >>= 1;
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;
+ goto BackOff;
} else {
- //
- // when no media, reset the MediaId to zero.
- //
- ScsiDiskDevice->BlkIo.Media->MediaId = 0;
+ *NeedRetry = FALSE;
+ return EFI_DEVICE_ERROR;
}
-
- *MediaChange = TRUE;
}
- return EFI_SUCCESS;
+ return ReturnStatus;
}
-
/**
- Send out Inquiry command to Device.
+ Submit Write(16) Command.
- @param ScsiDiskDevice The pointer of SCSI_DISK_DEV
- @param NeedRetry Indicates if needs try again when error happens
+ @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
- @retval EFI_DEVICE_ERROR Indicates that error occurs
- @retval EFI_SUCCESS Successfully to detect media
+ @return EFI_STATUS is returned by calling ScsiWrite16Command().
**/
EFI_STATUS
-ScsiDiskInquiryDevice (
- IN OUT SCSI_DISK_DEV *ScsiDiskDevice,
- OUT BOOLEAN *NeedRetry
+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
)
{
- UINT32 InquiryDataLength;
- UINT8 SenseDataLength;
- UINT8 HostAdapterStatus;
- UINT8 TargetStatus;
- EFI_SCSI_SENSE_DATA *SenseDataArray;
- UINTN NumberOfSenseKeys;
- EFI_STATUS Status;
- UINT8 MaxRetry;
- UINT8 Index;
+ EFI_STATUS Status;
+ EFI_STATUS ReturnStatus;
+ UINT8 SenseDataLength;
+ UINT8 HostAdapterStatus;
+ UINT8 TargetStatus;
+ UINTN Action;
- InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);
- SenseDataLength = 0;
+ //
+ // 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;
+ }
- Status = ScsiInquiryCommand (
- ScsiDiskDevice->ScsiIo,
- EFI_TIMER_PERIOD_SECONDS (1),
- NULL,
- &SenseDataLength,
- &HostAdapterStatus,
- &TargetStatus,
- (VOID *) &(ScsiDiskDevice->InquiryData),
- &InquiryDataLength,
- FALSE
- );
- //
- // no need to check HostAdapterStatus and TargetStatus
+ //
+ // 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
//
- if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
- ParseInquiryData (ScsiDiskDevice);
- 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;
@@ -926,1277 +4660,1760 @@ ScsiDiskInquiryDevice (
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 SubmitInquiryCommand() failed.
- // if ScsiDiskRequestSenseKeys() succeeds at last,
- // better retry SubmitInquiryCommand(). (by setting *NeedRetry = TRUE)
- //
- MaxRetry = 3;
- for (Index = 0; Index < MaxRetry; Index++) {
- Status = ScsiDiskRequestSenseKeys (
- ScsiDiskDevice,
- NeedRetry,
- &SenseDataArray,
- &NumberOfSenseKeys,
- TRUE
- );
- if (!EFI_ERROR (Status)) {
+
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {
+ DEBUG ((DEBUG_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));
+ 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;
+ }
- if (!*NeedRetry) {
+ //
+ // 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;
}
}
- //
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
- // set *NeedRetry = FALSE to avoid the outside caller try again.
- //
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+
+ return ReturnStatus;
}
/**
- To test deivice.
-
- 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
+ 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.
- @retval EFI_DEVICE_ERROR Indicates that error occurs
- @retval EFI_SUCCESS Successfully to test unit
+ @param Event The instance of EFI_EVENT.
+ @param Context The parameter passed in.
**/
-EFI_STATUS
-ScsiDiskTestUnitReady (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- OUT BOOLEAN *NeedRetry,
- OUT EFI_SCSI_SENSE_DATA **SenseDataArray,
- OUT UINTN *NumberOfSenseKeys
+VOID
+EFIAPI
+ScsiDiskNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
)
{
- EFI_STATUS Status;
- UINT8 SenseDataLength;
- UINT8 HostAdapterStatus;
- UINT8 TargetStatus;
- UINT8 Index;
- UINT8 MaxRetry;
+ 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;
- SenseDataLength = 0;
- *NumberOfSenseKeys = 0;
-
- //
- // Parameter 3 and 4: do not require sense data, retrieve it when needed.
- //
- Status = ScsiTestUnitReadyCommand (
- ScsiDiskDevice->ScsiIo,
- EFI_TIMER_PERIOD_SECONDS (1),
- NULL,
- &SenseDataLength,
- &HostAdapterStatus,
- &TargetStatus
- );
//
- // no need to check HostAdapterStatus and TargetStatus
+ // If previous sub-tasks already fails, no need to process this sub-task.
//
- 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;
+ if (Token->TransactionStatus != EFI_SUCCESS) {
+ goto Exit;
}
+
//
- // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)
+ // Check HostAdapterStatus and TargetStatus
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
//
-
- Status = CheckHostAdapterStatus (HostAdapterStatus);
+ Status = CheckHostAdapterStatus (Request->HostAdapterStatus);
if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
- *NeedRetry = TRUE;
- return EFI_DEVICE_ERROR;
-
+ 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);
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+ Token->TransactionStatus = EFI_DEVICE_ERROR;
+ goto Exit;
}
- Status = CheckTargetStatus (TargetStatus);
+ Status = CheckTargetStatus (Request->TargetStatus);
if (Status == EFI_NOT_READY) {
//
// reset the scsi device
//
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
- *NeedRetry = TRUE;
- return EFI_DEVICE_ERROR;
-
+ if (++Request->TimesRetry > MaxRetry) {
+ Token->TransactionStatus = EFI_DEVICE_ERROR;
+ goto Exit;
+ } else {
+ goto Retry;
+ }
} else if (Status == EFI_DEVICE_ERROR) {
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+ Token->TransactionStatus = EFI_DEVICE_ERROR;
+ goto Exit;
}
- MaxRetry = 3;
- for (Index = 0; Index < MaxRetry; Index++) {
- Status = ScsiDiskRequestSenseKeys (
- ScsiDiskDevice,
- NeedRetry,
- SenseDataArray,
- NumberOfSenseKeys,
- FALSE
- );
- if (!EFI_ERROR (Status)) {
- return EFI_SUCCESS;
- }
+ if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {
+ DEBUG ((DEBUG_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));
+
+ 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;
+ }
- if (!*NeedRetry) {
- return EFI_DEVICE_ERROR;
+ //
+ // 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;
}
}
+
//
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
- // set *NeedRetry = FALSE to avoid the outside caller try again.
+ // This sub-task succeeds, no need to retry.
//
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+ 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);
}
/**
- Parsing Sense Keys which got from request sense command.
+ 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().
- @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
+**/
+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;
- @retval EFI_DEVICE_ERROR Indicates that error occurs
- @retval EFI_SUCCESS Successfully to complete the parsing
+ 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
-DetectMediaParsingSenseKeys (
- OUT SCSI_DISK_DEV *ScsiDiskDevice,
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN NumberOfSenseKeys,
- OUT UINTN *Action
+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
)
{
- BOOLEAN RetryLater;
+ 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;
//
- // Default is to read capacity, unless..
+ // Create Event
//
- *Action = ACTION_READ_CAPACITY;
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ScsiDiskNotify,
+ Request,
+ &AsyncIoEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
- if (NumberOfSenseKeys == 0) {
- *Action = ACTION_NO_ACTION;
- return EFI_SUCCESS;
+ 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;
}
- if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
- //
- // No Sense Key returned from last submitted command
- //
- *Action = ACTION_NO_ACTION;
- return EFI_SUCCESS;
+ return EFI_SUCCESS;
+
+ErrorExit:
+ if (AsyncIoEvent != NULL) {
+ gBS->CloseEvent (AsyncIoEvent);
}
- if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {
- ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
- ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
- *Action = ACTION_NO_ACTION;
- return EFI_SUCCESS;
+ 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;
}
- if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
- ScsiDiskDevice->BlkIo.Media->MediaId++;
- return EFI_SUCCESS;
+ 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;
}
- if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
- ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
- ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
- return EFI_DEVICE_ERROR;
- }
+ return EFI_SUCCESS;
- if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
- return EFI_DEVICE_ERROR;
+ErrorExit:
+ if (AsyncIoEvent != NULL) {
+ gBS->CloseEvent (AsyncIoEvent);
}
- if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
- if (RetryLater) {
- *Action = ACTION_RETRY_COMMAND_LATER;
- return EFI_SUCCESS;
+ if (Request != NULL) {
+ if (Request->SenseData != NULL) {
+ FreePool (Request->SenseData);
}
- return EFI_DEVICE_ERROR;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ RemoveEntryList (&Request->Link);
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Request);
}
- return EFI_SUCCESS;
+ return Status;
}
-
/**
- 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
+ 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
-ScsiDiskReadCapacity (
- IN OUT SCSI_DISK_DEV *ScsiDiskDevice,
- OUT BOOLEAN *NeedRetry,
- OUT EFI_SCSI_SENSE_DATA **SenseDataArray,
- OUT UINTN *NumberOfSenseKeys
+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_SCSI_DISK_CAPACITY_DATA CapacityData;
- UINT32 DataLength;
- UINT8 HostAdapterStatus;
- UINT8 TargetStatus;
- EFI_STATUS CommandStatus;
- EFI_STATUS Status;
- UINT8 Index;
- UINT8 MaxRetry;
- UINT8 SenseDataLength;
+ EFI_STATUS Status;
+ SCSI_ASYNC_RW_REQUEST *Request;
+ EFI_EVENT AsyncIoEvent;
+ EFI_TPL OldTpl;
- SenseDataLength = 0;
- ZeroMem (&CapacityData, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
- DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
+ AsyncIoEvent = NULL;
- *NumberOfSenseKeys = 0;
- *NeedRetry = FALSE;
- //
- // submit Read Capacity Command. in this call,not request sense data
- //
- CommandStatus = ScsiReadCapacityCommand (
- ScsiDiskDevice->ScsiIo,
- EFI_TIMER_PERIOD_SECONDS (1),
- NULL,
- &SenseDataLength,
- &HostAdapterStatus,
- &TargetStatus,
- (VOID *) &CapacityData,
- &DataLength,
- FALSE
- );
- //
- // no need to check HostAdapterStatus and TargetStatus
- //
- if (CommandStatus == EFI_SUCCESS) {
- GetMediaInfo (ScsiDiskDevice, &CapacityData);
- return EFI_SUCCESS;
-
- } else 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;
+ Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));
+ if (Request == NULL) {
+ return EFI_OUT_OF_RESOURCES;
}
- Status = CheckTargetStatus (TargetStatus);
- if (Status == EFI_NOT_READY) {
- //
- // reset the scsi device
- //
- ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
- *NeedRetry = TRUE;
- return EFI_DEVICE_ERROR;
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+ InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);
+ gBS->RestoreTPL (OldTpl);
- } else if (Status == EFI_DEVICE_ERROR) {
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+ 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;
+
//
- // if goes here, meant SubmitReadCapacityCommand() failed.
- // if ScsiDiskRequestSenseKeys() succeeds at last,
- // better retry SubmitReadCapacityCommand(). (by setting *NeedRetry = TRUE)
+ // Create Event
//
- MaxRetry = 3;
- for (Index = 0; Index < MaxRetry; Index++) {
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ ScsiDiskNotify,
+ Request,
+ &AsyncIoEvent
+ );
+ if (EFI_ERROR (Status)) {
+ goto ErrorExit;
+ }
- Status = ScsiDiskRequestSenseKeys (
- ScsiDiskDevice,
- NeedRetry,
- SenseDataArray,
- NumberOfSenseKeys,
- TRUE
- );
- if (!EFI_ERROR (Status)) {
- *NeedRetry = TRUE;
- return EFI_DEVICE_ERROR;
- }
+ 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;
+ }
- if (!*NeedRetry) {
- return EFI_DEVICE_ERROR;
+ 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);
}
- //
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
- // set *NeedRetry = FALSE to avoid the outside caller try again.
- //
- *NeedRetry = FALSE;
- return EFI_DEVICE_ERROR;
+
+ return Status;
}
/**
- Check the HostAdapter status and re-interpret it in EFI_STATUS.
-
- @param HostAdapterStatus Host Adapter status
+ Check sense key to find if media presents.
- @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.
+ @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
**/
-EFI_STATUS
-CheckHostAdapterStatus (
- IN UINT8 HostAdapterStatus
+BOOLEAN
+ScsiDiskIsNoMedia (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
)
{
- 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;
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsNoMedia;
- 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;
+ IsNoMedia = FALSE;
+ SensePtr = SenseData;
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:
- return EFI_DEVICE_ERROR;
+ 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;
+ }
- default:
- return EFI_SUCCESS;
+ SensePtr++;
}
-}
+ return IsNoMedia;
+}
/**
- Check the target status and re-interpret it in EFI_STATUS.
+ Parse sense key.
- @param TargetStatus Target status
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA
+ @param SenseCounts The number of sense key
- @retval EFI_NOT_READY Device is NOT ready.
- @retval EFI_DEVICE_ERROR
- @retval EFI_SUCCESS
+ @retval TRUE Error
+ @retval FALSE NOT error
**/
-EFI_STATUS
-CheckTargetStatus (
- IN UINT8 TargetStatus
+BOOLEAN
+ScsiDiskIsMediaError (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
)
{
- 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;
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsError;
- 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;
+ IsError = FALSE;
+ SensePtr = SenseData;
- case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:
- return EFI_DEVICE_ERROR;
- break;
+ 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:
- default:
- return EFI_SUCCESS;
- }
-}
+ //
+ // 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;
-/**
- Retrieve all sense keys from the device.
+ default:
+ break;
+ }
- When encountering error during the process, if retrieve sense keys before
- error encounterred, it returns the sense keys with return status set to EFI_SUCCESS,
- and NeedRetry set to FALSE; otherwize, return the proper return status.
+ break;
- @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
+ 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;
- @retval EFI_DEVICE_ERROR Indicates that error occurs
- @retval EFI_SUCCESS Successfully to request sense key
+ default:
+ break;
+ }
-**/
-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;
+ break;
- FallStatus = EFI_SUCCESS;
- SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA);
+ default:
+ break;
+ }
- ZeroMem (
- ScsiDiskDevice->SenseData,
- sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)
- );
+ SensePtr++;
+ }
- *NumberOfSenseKeys = 0;
- *SenseDataArray = ScsiDiskDevice->SenseData;
- PtrSenseData = ScsiDiskDevice->SenseData;
+ return IsError;
+}
- for (SenseReq = TRUE; SenseReq;) {
- Status = ScsiRequestSenseCommand (
- ScsiDiskDevice->ScsiIo,
- EFI_TIMER_PERIOD_SECONDS (2),
- 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;
- }
+/**
+ Check sense key to find if hardware error happens.
- if (EFI_ERROR (FallStatus)) {
- if (*NumberOfSenseKeys != 0) {
- *NeedRetry = FALSE;
- return EFI_SUCCESS;
- } else {
- return EFI_DEVICE_ERROR;
- }
- }
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA
+ @param SenseCounts The number of sense key
- (*NumberOfSenseKeys) += 1;
+ @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++) {
//
- // no more sense key or number of sense keys exceeds predefined,
- // skip the loop.
+ // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)
//
- if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) ||
- (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {
- SenseReq = FALSE;
+ if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
+ IsError = TRUE;
}
- PtrSenseData += 1;
+
+ SensePtr++;
}
- return EFI_SUCCESS;
-}
+ return IsError;
+}
/**
- Get information from media read capacity command.
+ Check sense key to find if media has changed.
- @param ScsiDiskDevice The pointer of SCSI_DISK_DEV
- @param Capacity The pointer of EFI_SCSI_DISK_CAPACITY_DATA
+ @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.
**/
-VOID
-GetMediaInfo (
- IN OUT SCSI_DISK_DEV *ScsiDiskDevice,
- IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity
+BOOLEAN
+ScsiDiskIsMediaChange (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
)
{
- ScsiDiskDevice->BlkIo.Media->LastBlock = (Capacity->LastLba3 << 24) |
- (Capacity->LastLba2 << 16) |
- (Capacity->LastLba1 << 8) |
- Capacity->LastLba0;
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsMediaChanged;
- ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;
- ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity->BlockSize3 << 24) |
- (Capacity->BlockSize2 << 16) |
- (Capacity->BlockSize1 << 8) |
- Capacity->BlockSize0;
- if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;
- }
+ 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;
+ }
- if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
+ SensePtr++;
}
-}
-
-/**
- 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 ? 0 : 1);
- ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);
+ return IsMediaChanged;
}
/**
- Read sector from SCSI Disk.
+ Check sense key to find if reset happens.
- @param ScsiDiskDevice The poiniter 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 SenseData The pointer of EFI_SCSI_SENSE_DATA
+ @param SenseCounts The number of sense key
- @retval EFI_DEVICE_ERROR Indicates a device error.
- @retval EFI_SUCCESS Operation is successful.
+ @retval TRUE It is reset before.
+ @retval FALSE It is NOT reset before.
**/
-EFI_STATUS
-ScsiDiskReadSectors (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- OUT VOID *Buffer,
- IN EFI_LBA Lba,
- IN UINTN NumberOfBlocks
+BOOLEAN
+ScsiDiskIsResetBefore (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
)
{
- UINTN BlocksRemaining;
- UINT32 Lba32;
- UINT8 *PtrBuffer;
- UINT32 BlockSize;
- UINT32 ByteCount;
- UINT32 MaxBlock;
- UINT32 SectorCount;
- UINT64 Timeout;
- EFI_STATUS Status;
- UINT8 Index;
- UINT8 MaxRetry;
- BOOLEAN NeedRetry;
- EFI_SCSI_SENSE_DATA *SenseData;
- UINTN NumberOfSenseKeys;
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsResetBefore;
- SenseData = NULL;
- NumberOfSenseKeys = 0;
+ IsResetBefore = FALSE;
+ SensePtr = SenseData;
- Status = EFI_SUCCESS;
+ 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;
+ }
- BlocksRemaining = NumberOfBlocks;
- BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;
- //
- // limit the data bytes that can be transferred by one Read(10) Command
- //
- MaxBlock = 65536;
+ SensePtr++;
+ }
- PtrBuffer = Buffer;
- Lba32 = (UINT32) Lba;
+ return IsResetBefore;
+}
- while (BlocksRemaining > 0) {
+/**
+ Check sense key to find if the drive is ready.
- if (BlocksRemaining <= MaxBlock) {
+ @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
- SectorCount = (UINT16) BlocksRemaining;
- } else {
+ @retval TRUE Drive is ready.
+ @retval FALSE Drive is NOT ready.
- SectorCount = MaxBlock;
- }
+**/
+BOOLEAN
+ScsiDiskIsDriveReady (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts,
+ OUT BOOLEAN *RetryLater
+ )
+{
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN IsReady;
- ByteCount = SectorCount * BlockSize;
- Timeout = EFI_TIMER_PERIOD_SECONDS (2);
+ IsReady = TRUE;
+ *RetryLater = FALSE;
+ SensePtr = SenseData;
- MaxRetry = 2;
- for (Index = 0; Index < MaxRetry; Index++) {
+ 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;
+ }
- Status = ScsiDiskRead10 (
- ScsiDiskDevice,
- &NeedRetry,
- &SenseData,
- &NumberOfSenseKeys,
- Timeout,
- PtrBuffer,
- &ByteCount,
- Lba32,
- SectorCount
- );
- if (!EFI_ERROR (Status)) {
break;
- }
-
- if (!NeedRetry) {
- return EFI_DEVICE_ERROR;
- }
-
- }
- if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
- return EFI_DEVICE_ERROR;
+ default:
+ break;
}
- //
- // actual transferred sectors
- //
- SectorCount = ByteCount / BlockSize;
-
- Lba32 += SectorCount;
- PtrBuffer = PtrBuffer + SectorCount * BlockSize;
- BlocksRemaining -= SectorCount;
+ SensePtr++;
}
- return EFI_SUCCESS;
+ return IsReady;
}
/**
- Write sector to SCSI Disk.
+ Check sense key to find if it has sense key.
- @param ScsiDiskDevice The poiniter 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 SenseData - The pointer of EFI_SCSI_SENSE_DATA
+ @param SenseCounts - The number of sense key
- @retval EFI_DEVICE_ERROR Indicates a device error.
- @retval EFI_SUCCESS Operation is successful.
+ @retval TRUE It has sense key.
+ @retval FALSE It has NOT any sense key.
**/
-EFI_STATUS
-ScsiDiskWriteSectors (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- IN VOID *Buffer,
- IN EFI_LBA Lba,
- IN UINTN NumberOfBlocks
+BOOLEAN
+ScsiDiskHaveSenseKey (
+ IN EFI_SCSI_SENSE_DATA *SenseData,
+ IN UINTN SenseCounts
)
{
- UINTN BlocksRemaining;
- UINT32 Lba32;
- UINT8 *PtrBuffer;
- UINT32 BlockSize;
- UINT32 ByteCount;
- UINT32 MaxBlock;
- UINT32 SectorCount;
- UINT64 Timeout;
- EFI_STATUS Status;
- UINT8 Index;
- UINT8 MaxRetry;
- BOOLEAN NeedRetry;
- EFI_SCSI_SENSE_DATA *SenseData;
- UINTN NumberOfSenseKeys;
-
- SenseData = NULL;
- NumberOfSenseKeys = 0;
-
- Status = EFI_SUCCESS;
-
- BlocksRemaining = NumberOfBlocks;
- BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;
- //
- // limit the data bytes that can be transferred by one Write(10) Command
- //
- MaxBlock = 65536;
-
- PtrBuffer = Buffer;
- Lba32 = (UINT32) Lba;
-
- while (BlocksRemaining > 0) {
+ EFI_SCSI_SENSE_DATA *SensePtr;
+ UINTN Index;
+ BOOLEAN HaveSenseKey;
- if (BlocksRemaining <= MaxBlock) {
-
- SectorCount = (UINT16) BlocksRemaining;
- } else {
-
- SectorCount = MaxBlock;
- }
-
- ByteCount = SectorCount * BlockSize;
- Timeout = EFI_TIMER_PERIOD_SECONDS (2);
- MaxRetry = 2;
- for (Index = 0; Index < MaxRetry; Index++) {
- Status = ScsiDiskWrite10 (
- ScsiDiskDevice,
- &NeedRetry,
- &SenseData,
- &NumberOfSenseKeys,
- Timeout,
- PtrBuffer,
- &ByteCount,
- Lba32,
- SectorCount
- );
- if (!EFI_ERROR (Status)) {
- break;
- }
+ if (SenseCounts == 0) {
+ HaveSenseKey = FALSE;
+ } else {
+ HaveSenseKey = TRUE;
+ }
- if (!NeedRetry) {
- return EFI_DEVICE_ERROR;
- }
- }
+ SensePtr = SenseData;
- if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
- return EFI_DEVICE_ERROR;
- }
+ for (Index = 0; Index < SenseCounts; Index++) {
//
- // actual transferred sectors
+ // Sense Key is SK_NO_SENSE (0x0)
//
- SectorCount = ByteCount / BlockSize;
+ if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&
+ (Index == 0))
+ {
+ HaveSenseKey = FALSE;
+ }
- Lba32 += SectorCount;
- PtrBuffer = PtrBuffer + SectorCount * BlockSize;
- BlocksRemaining -= SectorCount;
+ SensePtr++;
}
- return EFI_SUCCESS;
+ return HaveSenseKey;
}
-
/**
- Sumbmit Read command.
+ Release resource about disk device.
- @param ScsiDiskDevice The pointer of ScsiDiskDevice
- @param NeedRetry The pointer of flag indicates if needs retry if error happens
- @param SenseDataArray NOT used yet in this function
- @param NumberOfSenseKeys The number of sense key
- @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 SectorSize The size of sector
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV
- @return EFI_STATUS is returned by calling ScsiRead10Command().
**/
-EFI_STATUS
-ScsiDiskRead10 (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- OUT BOOLEAN *NeedRetry,
- OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
- OUT UINTN *NumberOfSenseKeys,
- IN UINT64 Timeout,
- OUT UINT8 *DataBuffer,
- IN OUT UINT32 *DataLength,
- IN UINT32 StartLba,
- IN UINT32 SectorSize
+VOID
+ReleaseScsiDiskDeviceResources (
+ IN SCSI_DISK_DEV *ScsiDiskDevice
)
{
- UINT8 SenseDataLength;
- EFI_STATUS Status;
- UINT8 HostAdapterStatus;
- UINT8 TargetStatus;
+ if (ScsiDiskDevice == NULL) {
+ return;
+ }
- *NeedRetry = FALSE;
- *NumberOfSenseKeys = 0;
- SenseDataLength = 0;
- Status = ScsiRead10Command (
- ScsiDiskDevice->ScsiIo,
- Timeout,
- NULL,
- &SenseDataLength,
- &HostAdapterStatus,
- &TargetStatus,
- DataBuffer,
- DataLength,
- StartLba,
- SectorSize
- );
- return Status;
-}
+ if (ScsiDiskDevice->SenseData != NULL) {
+ FreePool (ScsiDiskDevice->SenseData);
+ ScsiDiskDevice->SenseData = NULL;
+ }
+
+ if (ScsiDiskDevice->ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);
+ ScsiDiskDevice->ControllerNameTable = NULL;
+ }
+
+ FreePool (ScsiDiskDevice);
+ ScsiDiskDevice = NULL;
+}
/**
- Submit Write Command.
+ Determine if Block Io & Block Io2 should be produced.
- @param ScsiDiskDevice The pointer of ScsiDiskDevice
- @param NeedRetry The pointer of flag indicates if needs retry if error happens
- @param SenseDataArray NOT used yet in this function
- @param NumberOfSenseKeys The number of sense key
- @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 SectorSize The size of sector
- @return EFI_STATUS is returned by calling ScsiWrite10Command().
+ @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.
**/
-EFI_STATUS
-ScsiDiskWrite10 (
- IN SCSI_DISK_DEV *ScsiDiskDevice,
- OUT BOOLEAN *NeedRetry,
- OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL
- OUT UINTN *NumberOfSenseKeys,
- IN UINT64 Timeout,
- IN UINT8 *DataBuffer,
- IN OUT UINT32 *DataLength,
- IN UINT32 StartLba,
- IN UINT32 SectorSize
+BOOLEAN
+DetermineInstallBlockIo (
+ IN EFI_HANDLE ChildHandle
)
{
- EFI_STATUS Status;
- UINT8 SenseDataLength;
- UINT8 HostAdapterStatus;
- UINT8 TargetStatus;
+ EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
- *NeedRetry = FALSE;
- *NumberOfSenseKeys = 0;
- SenseDataLength = 0;
- Status = ScsiWrite10Command (
- ScsiDiskDevice->ScsiIo,
- Timeout,
- NULL,
- &SenseDataLength,
- &HostAdapterStatus,
- &TargetStatus,
- DataBuffer,
- DataLength,
- StartLba,
- SectorSize
- );
- return Status;
-}
+ //
+ // 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;
+}
/**
- Check sense key to find if media presents.
+ 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 SenseData The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts The number of sense key
- @retval TRUE NOT any media
- @retval FALSE Media presents
+ @param ProtocolGuid ProtocolGuid pointer.
+ @param ChildHandle Child Handle to retrieve Parent information.
+
**/
-BOOLEAN
-ScsiDiskIsNoMedia (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+VOID *
+EFIAPI
+GetParentProtocol (
+ IN EFI_GUID *ProtocolGuid,
+ IN EFI_HANDLE ChildHandle
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN IsNoMedia;
+ UINTN Index;
+ UINTN HandleCount;
+ VOID *Interface;
+ EFI_STATUS Status;
+ EFI_HANDLE *HandleBuffer;
- IsNoMedia = FALSE;
- SensePtr = SenseData;
+ //
+ // Retrieve the list of all handles from the handle database
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ ProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
- 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;
+ 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;
+ }
}
- SensePtr++;
}
- return IsNoMedia;
+ gBS->FreePool (HandleBuffer);
+ return NULL;
}
-
/**
- Parse sense key.
+ Determine if EFI Erase Block Protocol should be produced.
- @param SenseData The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts The number of sense key
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.
+ @param ChildHandle Handle of device.
- @retval TRUE Error
- @retval FALSE NOT error
+ @retval TRUE Should produce EFI Erase Block Protocol.
+ @retval FALSE Should not produce EFI Erase Block Protocol.
**/
BOOLEAN
-ScsiDiskIsMediaError (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+DetermineInstallEraseBlock (
+ IN SCSI_DISK_DEV *ScsiDiskDevice,
+ IN EFI_HANDLE ChildHandle
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN IsError;
-
- IsError = FALSE;
- SensePtr = SenseData;
+ 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;
- for (Index = 0; Index < SenseCounts; Index++) {
+ //
+ // UNMAP command is not supported by any of the UFS WLUNs.
+ //
+ if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) {
+ RetVal = FALSE;
+ goto Done;
+ }
- switch (SensePtr->Sense_Key) {
+ Status = gBS->HandleProtocol (
+ ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **)&DevicePathNode
+ );
+ //
+ // Device Path protocol must be installed on the device handle.
+ //
+ ASSERT_EFI_ERROR (Status);
- case EFI_SCSI_SK_MEDIUM_ERROR:
- //
- // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)
- //
- switch (SensePtr->Addnl_Sense_Code) {
+ 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;
+ }
- //
- // fall through
- //
- case EFI_SCSI_ASC_MEDIA_ERR1:
+ DevicePathNode = NextDevicePathNode (DevicePathNode);
+ }
- //
- // fall through
- //
- case EFI_SCSI_ASC_MEDIA_ERR2:
+ if (!UfsDevice) {
+ RetVal = FALSE;
+ goto Done;
+ }
- //
- // fall through
- //
- case EFI_SCSI_ASC_MEDIA_ERR3:
- case EFI_SCSI_ASC_MEDIA_ERR4:
- IsError = TRUE;
- break;
+ //
+ // 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;
+ }
- default:
- break;
- }
+ SenseDataLength = 0;
+ DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);
+ ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
- break;
+ CommandStatus = ScsiReadCapacity16Command (
+ ScsiDiskDevice->ScsiIo,
+ SCSI_DISK_TIMEOUT,
+ NULL,
+ &SenseDataLength,
+ &HostAdapterStatus,
+ &TargetStatus,
+ (VOID *)CapacityData16,
+ &DataLength16,
+ FALSE
+ );
- 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;
+ 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 ((
+ DEBUG_VERBOSE,
+ "ScsiDisk EraseBlock: Either TPE or TPRZ is not set: 0x%x.\n",
+ CapacityData16->LowestAlignLogic2
+ ));
+
+ RetVal = FALSE;
+ goto Done;
+ }
+ } else {
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "ScsiDisk EraseBlock: ReadCapacity16 failed with status %r.\n",
+ CommandStatus
+ ));
- default:
- break;
- }
- break;
+ RetVal = FALSE;
+ goto Done;
+ }
- default:
- break;
- }
+ //
+ // Check whether the UFS device server implements the UNMAP command.
+ //
+ if ((ScsiDiskDevice->UnmapInfo.MaxLbaCnt == 0) ||
+ (ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt == 0))
+ {
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "ScsiDisk EraseBlock: The device server does not implement the UNMAP command.\n"
+ ));
+
+ RetVal = FALSE;
+ goto Done;
+ }
- SensePtr++;
+Done:
+ if (CapacityData16 != NULL) {
+ FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
}
- return IsError;
+ return RetVal;
}
-
/**
- Check sense key to find if hardware error happens.
+ Determine if EFI Storage Security Command Protocol should be produced.
- @param SenseData The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts The number of sense key
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.
+ @param ChildHandle Handle of device.
- @retval TRUE Hardware error exits.
- @retval FALSE NO error.
+ @retval TRUE Should produce EFI Storage Security Command Protocol.
+ @retval FALSE Should not produce EFI Storage Security Command Protocol.
**/
BOOLEAN
-ScsiDiskIsHardwareError (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+DetermineInstallStorageSecurity (
+ IN SCSI_DISK_DEV *ScsiDiskDevice,
+ IN EFI_HANDLE ChildHandle
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN IsError;
-
- IsError = FALSE;
- SensePtr = SenseData;
+ 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);
- for (Index = 0; Index < SenseCounts; Index++) {
-
+ while (!IsDevicePathEndType (DevicePathNode)) {
//
- // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)
+ // For now, only support Storage Security Command Protocol on UFS devices.
//
- if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
- IsError = TRUE;
+ if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&
+ (DevicePathNode->SubType == MSG_UFS_DP))
+ {
+ UfsDevice = (UFS_DEVICE_PATH *)DevicePathNode;
+ break;
}
- SensePtr++;
+ DevicePathNode = NextDevicePathNode (DevicePathNode);
}
- return IsError;
-}
+ if (UfsDevice == NULL) {
+ RetVal = FALSE;
+ goto Done;
+ }
+
+ if (UfsDevice->Lun != UFS_WLUN_RPMB) {
+ RetVal = FALSE;
+ }
+Done:
+ return RetVal;
+}
/**
- Check sense key to find if media has changed.
+ Provides inquiry information for the controller type.
- @param SenseData The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts The number of sense key
+ 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
- @retval TRUE Media is changed.
- @retval FALSE Medit is NOT changed.
**/
-BOOLEAN
-ScsiDiskIsMediaChange (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+EFI_STATUS
+EFIAPI
+ScsiDiskInfoInquiry (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *InquiryData,
+ IN OUT UINT32 *InquiryDataSize
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN IsMediaChanged;
+ EFI_STATUS Status;
+ SCSI_DISK_DEV *ScsiDiskDevice;
- 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;
- }
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);
- SensePtr++;
+ Status = EFI_BUFFER_TOO_SMALL;
+ if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {
+ Status = EFI_SUCCESS;
+ CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));
}
- return IsMediaChanged;
+ *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);
+ return Status;
}
/**
- Check sense key to find if reset happens.
+ Provides identify information for the controller type.
- @param SenseData The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts The number of sense key
+ This function is used by the IDE bus driver to get identify data. Data format
+ of Identify data is defined by the Interface GUID.
- @retval TRUE It is reset before.
- @retval FALSE It is NOT reset before.
+ @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
**/
-BOOLEAN
-ScsiDiskIsResetBefore (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+EFI_STATUS
+EFIAPI
+ScsiDiskInfoIdentify (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *IdentifyData,
+ IN OUT UINT32 *IdentifyDataSize
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN IsResetBefore;
-
- IsResetBefore = FALSE;
- SensePtr = SenseData;
+ EFI_STATUS Status;
+ SCSI_DISK_DEV *ScsiDiskDevice;
- for (Index = 0; Index < SenseCounts; Index++) {
-
+ if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {
//
- // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)
- // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)
+ // Physical SCSI bus does not support this data class.
//
- if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
- (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {
- IsResetBefore = TRUE;
- }
+ return EFI_NOT_FOUND;
+ }
- SensePtr++;
+ 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));
}
- return IsResetBefore;
+ *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);
+ return Status;
}
/**
- Check sense key to find if the drive is ready.
+ Provides sense data information for the controller type.
- @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
+ This function is used by the IDE bus driver to get sense data.
+ Data format of Sense data is defined by the Interface GUID.
- @retval TRUE Drive is ready.
- @retval FALSE Drive is NOT ready.
+ @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.
**/
-BOOLEAN
-ScsiDiskIsDriveReady (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts,
- OUT BOOLEAN *RetryLater
+EFI_STATUS
+EFIAPI
+ScsiDiskInfoSenseData (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ IN OUT VOID *SenseData,
+ IN OUT UINT32 *SenseDataSize,
+ OUT UINT8 *SenseDataNumber
)
{
- 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) {
+ return EFI_NOT_FOUND;
+}
- 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;
+/**
+ This function is used by the IDE bus driver to get controller information.
- default:
- IsReady = FALSE;
- *RetryLater = FALSE;
- break;
- }
- break;
+ @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.
- default:
- break;
- }
- break;
+ @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.
+ @retval EFI_UNSUPPORTED This is not an IDE device.
- default:
- break;
- }
+**/
+EFI_STATUS
+EFIAPI
+ScsiDiskInfoWhichIde (
+ IN EFI_DISK_INFO_PROTOCOL *This,
+ OUT UINT32 *IdeChannel,
+ OUT UINT32 *IdeDevice
+ )
+{
+ SCSI_DISK_DEV *ScsiDiskDevice;
- SensePtr++;
+ if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {
+ //
+ // This is not an IDE physical device.
+ //
+ return EFI_UNSUPPORTED;
}
- return IsReady;
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);
+ *IdeChannel = ScsiDiskDevice->Channel;
+ *IdeDevice = ScsiDiskDevice->Device;
+
+ return EFI_SUCCESS;
}
/**
- Check sense key to find if it has sense key.
+ Issues ATA IDENTIFY DEVICE command to identify ATAPI device.
- @param SenseData - The pointer of EFI_SCSI_SENSE_DATA
- @param SenseCounts - The number of sense key
+ 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.
- @retval TRUE It has sense key.
- @retval FALSE It has NOT any sense key.
+ @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.
**/
-BOOLEAN
-ScsiDiskHaveSenseKey (
- IN EFI_SCSI_SENSE_DATA *SenseData,
- IN UINTN SenseCounts
+EFI_STATUS
+AtapiIdentifyDevice (
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice
)
{
- EFI_SCSI_SENSE_DATA *SensePtr;
- UINTN Index;
- BOOLEAN HaveSenseKey;
-
- if (SenseCounts == 0) {
- HaveSenseKey = FALSE;
- } else {
- HaveSenseKey = TRUE;
- }
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
+ UINT8 Cdb[6];
- 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;
- }
+ //
+ // Initialize SCSI REQUEST_PACKET and 6-byte Cdb
+ //
+ ZeroMem (&CommandPacket, sizeof (CommandPacket));
+ ZeroMem (Cdb, sizeof (Cdb));
- SensePtr++;
- }
+ 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 HaveSenseKey;
+ return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);
}
/**
- Release resource about disk device.
+ Initialize the installation of DiskInfo protocol.
- @param ScsiDiskDevice The pointer of SCSI_DISK_DEV
+ 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
-ReleaseScsiDiskDeviceResources (
- IN SCSI_DISK_DEV *ScsiDiskDevice
+InitializeInstallDiskInfo (
+ IN SCSI_DISK_DEV *ScsiDiskDevice,
+ IN EFI_HANDLE ChildHandle
)
{
- if (ScsiDiskDevice == NULL) {
- return ;
- }
-
- if (ScsiDiskDevice->SenseData != NULL) {
- gBS->FreePool (ScsiDiskDevice->SenseData);
- ScsiDiskDevice->SenseData = NULL;
- }
+ 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;
+ }
- if (ScsiDiskDevice->ControllerNameTable != NULL) {
- FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);
- ScsiDiskDevice->ControllerNameTable = NULL;
+ DevicePathNode = ChildDevicePathNode;
}
- gBS->FreePool (ScsiDiskDevice);
-
- ScsiDiskDevice = NULL;
+ return;
}