/** @file
ScsiDisk.c - UEFI SCSI Disk Driver
This driver manages SCSI hard disk devices. It produces:
- EFI_BLOCK_IO_PROTOCOL
- EFI_DISK_IO_PROTOCOL
- EFI_ERASE_BLOCK_PROTOCOL
on child handles created when a SCSI I/O protocol is detected on a
controller handle.
Derived from reverse engineering of ScsiDisk.efi (MD5: 5ca30228dfaec7f138f04c586d10b153).
Source: MdeModulePkg\Bus\Scsi\ScsiDiskDxe\ScsiDisk.c
Build: HR6N0XMLK DEBUG_VS2015 X64
NOTE: This is a reconstructed source based on static analysis of the
compiled binary. Function signatures and logic are inferred from
observed behavior in the disassembly.
**/
#include "ScsiDisk.h"
//
// Global data (from .data section, 0x88A0-0x8AA0)
//
EFI_HANDLE gImageHandle = NULL; // 0x8A70
EFI_SYSTEM_TABLE *gST = NULL; // 0x8A60
EFI_BOOT_SERVICES *gBS = NULL; // 0x8A68
EFI_RUNTIME_SERVICES *gRT = NULL; // 0x8A78
//
// Component Name tables (in .data at 0x8A10-0x8A58)
//
STATIC EFI_UNICODE_STRING_TABLE mScsiDiskDriverNameTable[] = {
{ "eng;en", L"Scsi Disk Driver" },
{ NULL, NULL }
};
STATIC EFI_UNICODE_STRING_TABLE mScsiDiskControllerNameTable[] = {
{ "eng;en", L"SCSI Disk Device" },
{ NULL, NULL }
};
//
// EFI_DRIVER_BINDING_PROTOCOL instance (at 0x88C0 in .data)
//
STATIC EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
ScsiDiskDriverBindingSupported,
ScsiDiskDriverBindingStart,
ScsiDiskDriverBindingStop,
0x10,
NULL,
NULL
};
//
// EFI_COMPONENT_NAME2_PROTOCOL instance (at 0x88E0 in .data)
//
STATIC EFI_COMPONENT_NAME2_PROTOCOL gScsiDiskComponentName2 = {
ScsiDiskComponentNameGetDriverName,
ScsiDiskComponentNameGetControllerName,
"en"
};
//
// EFI_DRIVER_CONFIGURATION_PROTOCOL instance (at 0x8900 in .data)
//
STATIC EFI_DRIVER_CONFIGURATION_PROTOCOL gScsiDiskDriverConfiguration = {
NULL,
NULL,
NULL
};
//
// GUID definitions
//
EFI_GUID gEfiScsiIoProtocolGuid = SCSI_DISK_SCSI_IO_GUID;
EFI_GUID gEfiBlockIoProtocolGuid = SCSI_DISK_BLOCK_IO_GUID;
EFI_GUID gEfiDiskIoProtocolGuid = SCSI_DISK_DISK_IO_GUID;
EFI_GUID gEfiDriverBindingProtocolGuid = SCSI_DISK_DRIVER_BINDING_GUID;
EFI_GUID gEfiStorageSecurityCommandProtocolGuid = STORAGE_SECURITY_COMMAND_PROTOCOL_GUID;
//
// Forward declarations of local helper functions
//
STATIC
EFI_STATUS
ScsiDiskReadSectors (
IN SCSI_DISK_DEVICE *Device,
IN UINT32 MediaId,
IN EFI_LBA Lba,
OUT VOID *Buffer,
IN UINTN BufferSize
);
STATIC
EFI_STATUS
ScsiDiskWriteSectors (
IN SCSI_DISK_DEVICE *Device,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN VOID *Buffer,
IN UINTN BufferSize
);
STATIC
EFI_STATUS
ScsiDiskReadCapacity (
IN SCSI_DISK_DEVICE *Device
);
STATIC
VOID
ScsiDiskParseCapacityData (
IN SCSI_DISK_DEVICE *Device,
IN UINT8 *Data,
IN BOOLEAN IsCapacity16
);
//
// Module Entry Point
//
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
gST = (VOID *)0; // Set by _ModuleEntryPoint thunk
gBS = (VOID *)0; // Set by _ModuleEntryPoint thunk
gRT = (VOID *)0; // Set by _ModuleEntryPoint thunk
gImageHandle = ImageHandle;
Status = ScsiDiskDriverEntry (ImageHandle);
return Status;
}
//
// Driver Entry Point
//
EFI_STATUS
EFIAPI
ScsiDiskDriverEntry (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
VOID *Interface;
gImageHandle = ImageHandle;
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiDriverBindingProtocolGuid, &gScsiDiskDriverBinding,
&gEfiComponentName2ProtocolGuid, &gScsiDiskComponentName2,
&gEfiDriverConfigurationProtocolGuid, &Interface,
NULL
);
return Status;
}
//
// Driver Binding Protocol
//
EFI_STATUS
EFIAPI
ScsiDiskDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
VOID *Interface;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiScsiIoProtocolGuid,
&Interface,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
gBS->CloseProtocol (
ControllerHandle,
&gEfiScsiIoProtocolGuid,
gImageHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ScsiDiskDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
SCSI_DISK_DEVICE *Device;
BOOLEAN MediaChanged;
UINT32 Retry;
//
// Allocate zero-initialized device structure (0x390 bytes)
//
Device = (SCSI_DISK_DEVICE *)gBS->AllocateZeroPool (SCSI_DISK_DEVICE_SIZE);
if (Device == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Device->Signature = SCSI_DISK_DEVICE_SIGNATURE;
ScsiDiskInitializeListHead (&Device->AsyncUnmapQueue);
//
// Open SCSI I/O protocol on the controller handle
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiScsiIoProtocolGuid,
(VOID **)&Device->ScsiIo,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
gBS->FreePool (Device);
return Status;
}
Device->ControllerHandle = ControllerHandle;
Device->MediaPresentFlag = 1;
//
// Retry loop for media detection
//
for (Retry = 0; Retry < 3; Retry++) {
Status = ScsiDiskDetectMedia (Device, FALSE, &MediaChanged);
if (!EFI_ERROR (Status)) {
break;
}
if (Status == EFI_NO_MEDIA) {
//
// No media present -- still create device node, just with no media
//
break;
}
gBS->Stall (100000);
}
//
// Set up Block I/O protocol function pointers (inlined in structure)
//
Device->BlkIoRevision = 0x200031;
Device->BlkIoMediaPtr = &Device->Media;
Device->BlkIoReset = ScsiDiskReset;
Device->BlkIoReadBlocks = ScsiDiskReadBlocks;
Device->BlkIoWriteBlocks = ScsiDiskWriteBlocks;
Device->BlkIoFlushBlocks = ScsiDiskFlushBlocks;
Device->BlkIoMediaPtr2 = &Device->Media;
//
// Set up Disk I/O protocol function pointers (inlined in structure)
//
Device->DiskIoReset = ScsiDiskDiskIoReset;
Device->DiskIoRead = ScsiDiskDiskIoRead;
Device->DiskIoWrite = ScsiDiskDiskIoWrite;
Device->DiskIoFlush = ScsiDiskDiskIoFlush;
//
// Fill in media descriptor
//
Device->Media.RemovableMedia = (UINT8)ScsiDiskIsDeviceRemovable (Device, ControllerHandle);
Device->Media.MediaPresent = Device->MediaPresentFlag ? 1 : 0;
Device->Media.LogicalPartition = 0;
Device->Media.ReadOnly = 0;
Device->Media.WriteCaching = 0;
Device->Media.BlockSize = 0x200;
Device->Media.IoAlign = 0;
Device->Media.LastBlock = 0;
Device->Media.LowestAlignedLBA = 0;
Device->Media.LogicalUnitsPerPhysicalBlockExponent = 0;
Device->Media.OptimalTransferLengthGranularity = 0;
//
// Install Block I/O and Disk I/O protocols on the controller handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ControllerHandle,
&gEfiBlockIoProtocolGuid, &Device->BlockIo,
&gEfiDiskIoProtocolGuid, &Device->DiskIo,
NULL
);
if (EFI_ERROR (Status)) {
gBS->CloseProtocol (
ControllerHandle,
&gEfiScsiIoProtocolGuid,
gImageHandle,
ControllerHandle
);
gBS->FreePool (Device);
return Status;
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ScsiDiskDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
SCSI_DISK_DEVICE *Device;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
UINTN Index;
for (Index = 0; Index < NumberOfChildren; Index++) {
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
continue;
}
Device = SCSI_DISK_DEVICE_FROM_BLOCK_IO (BlockIo);
gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid, &Device->BlockIo,
&gEfiDiskIoProtocolGuid, &Device->DiskIo,
NULL
);
gBS->CloseProtocol (
Device->ControllerHandle,
&gEfiScsiIoProtocolGuid,
gImageHandle,
Device->ControllerHandle
);
ScsiDiskFreeDeviceMemory (Device);
}
return EFI_SUCCESS;
}
//
// Block I/O Protocol
//
EFI_STATUS
EFIAPI
ScsiDiskReset (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
SCSI_DISK_DEVICE *Device;
Device = SCSI_DISK_DEVICE_FROM_BLOCK_IO (This);
if (ExtendedVerification) {
Device->ScsiIo->ResetBus (Device->ScsiIo);
}
return Device->ScsiIo->ResetDevice (Device->ScsiIo);
}
EFI_STATUS
EFIAPI
ScsiDiskReadBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
SCSI_DISK_DEVICE *Device;
EFI_STATUS Status;
BOOLEAN MediaChanged;
Device = SCSI_DISK_DEVICE_FROM_BLOCK_IO (This);
Status = ScsiDiskDetectMedia (Device, TRUE, &MediaChanged);
if (EFI_ERROR (Status)) {
return Status;
}
if (MediaChanged) {
return EFI_MEDIA_CHANGED;
}
if (!Device->Media.MediaPresent) {
return EFI_NO_MEDIA;
}
if (MediaId != Device->Media.MediaId) {
return EFI_MEDIA_CHANGED;
}
if ((BufferSize % Device->Media.BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (LBA + (BufferSize / Device->Media.BlockSize) > Device->Media.LastBlock + 1) {
return EFI_INVALID_PARAMETER;
}
return ScsiDiskReadSectors (Device, MediaId, LBA, Buffer, BufferSize);
}
EFI_STATUS
EFIAPI
ScsiDiskWriteBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
SCSI_DISK_DEVICE *Device;
EFI_STATUS Status;
BOOLEAN MediaChanged;
Device = SCSI_DISK_DEVICE_FROM_BLOCK_IO (This);
Status = ScsiDiskDetectMedia (Device, TRUE, &MediaChanged);
if (EFI_ERROR (Status)) {
return Status;
}
if (MediaChanged) {
return EFI_MEDIA_CHANGED;
}
if (!Device->Media.MediaPresent) {
return EFI_NO_MEDIA;
}
if (MediaId != Device->Media.MediaId) {
return EFI_MEDIA_CHANGED;
}
if (Device->Media.ReadOnly) {
return EFI_WRITE_PROTECTED;
}
if ((BufferSize % Device->Media.BlockSize) != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (LBA + (BufferSize / Device->Media.BlockSize) > Device->Media.LastBlock + 1) {
return EFI_INVALID_PARAMETER;
}
return ScsiDiskWriteSectors (Device, MediaId, LBA, Buffer, BufferSize);
}
EFI_STATUS
EFIAPI
ScsiDiskFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
//
// Disk I/O Protocol
//
EFI_STATUS
EFIAPI
ScsiDiskDiskIoReset (
IN EFI_DISK_IO_PROTOCOL *This,
IN UINT64 Offset
)
{
EFI_STATUS Status;
SCSI_DISK_DEVICE *Device;
Device = CR (This, SCSI_DISK_DEVICE, DiskIoReset, SCSI_DISK_DEVICE_SIGNATURE);
Status = Device->BlkIoReset (&Device->BlockIo, TRUE);
return Status;
}
EFI_STATUS
EFIAPI
ScsiDiskDiskIoRead (
IN EFI_DISK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT64 Offset,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
SCSI_DISK_DEVICE *Device;
EFI_BLOCK_IO_MEDIA *Media;
UINT64 Lba;
UINTN BlockSize;
UINT8 *DataBuffer;
UINTN DataSize;
UINTN OffsetInBlock;
EFI_STATUS Status;
Device = CR (This, SCSI_DISK_DEVICE, DiskIoRead, SCSI_DISK_DEVICE_SIGNATURE);
Media = &Device->Media;
BlockSize = Media->BlockSize;
Lba = Offset / BlockSize;
OffsetInBlock = (UINTN)(Offset % BlockSize);
//
// Allocate intermediate buffer for block-aligned read
//
DataSize = ((OffsetInBlock + BufferSize + BlockSize - 1) / BlockSize) * BlockSize;
DataBuffer = (UINT8 *)gBS->AllocatePool (DataSize);
if (DataBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Device->BlkIoReadBlocks (&Device->BlockIo, MediaId, Lba, DataSize, DataBuffer);
if (EFI_ERROR (Status)) {
gBS->FreePool (DataBuffer);
return Status;
}
gBS->CopyMem (Buffer, DataBuffer + OffsetInBlock, BufferSize);
gBS->FreePool (DataBuffer);
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ScsiDiskDiskIoWrite (
IN EFI_DISK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN UINT64 Offset,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
SCSI_DISK_DEVICE *Device;
EFI_BLOCK_IO_MEDIA *Media;
UINT64 Lba;
UINTN BlockSize;
UINT8 *DataBuffer;
UINTN DataSize;
UINTN OffsetInBlock;
EFI_STATUS Status;
BOOLEAN NeedRead;
Device = CR (This, SCSI_DISK_DEVICE, DiskIoWrite, SCSI_DISK_DEVICE_SIGNATURE);
Media = &Device->Media;
BlockSize = Media->BlockSize;
Lba = Offset / BlockSize;
OffsetInBlock = (UINTN)(Offset % BlockSize);
DataSize = ((OffsetInBlock + BufferSize + BlockSize - 1) / BlockSize) * BlockSize;
NeedRead = (OffsetInBlock != 0) || (((OffsetInBlock + BufferSize) % BlockSize) != 0);
DataBuffer = (UINT8 *)gBS->AllocatePool (DataSize);
if (DataBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if (NeedRead) {
Status = Device->BlkIoReadBlocks (&Device->BlockIo, MediaId, Lba, DataSize, DataBuffer);
if (EFI_ERROR (Status)) {
gBS->FreePool (DataBuffer);
return Status;
}
} else {
gBS->ZeroMem (DataBuffer, DataSize);
}
gBS->CopyMem (DataBuffer + OffsetInBlock, Buffer, BufferSize);
Status = Device->BlkIoWriteBlocks (&Device->BlockIo, MediaId, Lba, DataSize, DataBuffer);
gBS->FreePool (DataBuffer);
return Status;
}
EFI_STATUS
EFIAPI
ScsiDiskDiskIoFlush (
IN EFI_DISK_IO_PROTOCOL *This
)
{
return EFI_SUCCESS;
}
//
// Internal SCSI Command Functions
//
STATIC
EFI_STATUS
ScsiDiskReadSectors (
IN SCSI_DISK_DEVICE *Device,
IN UINT32 MediaId,
IN EFI_LBA Lba,
OUT VOID *Buffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
UINT8 Cdb[16];
UINT32 BlockSize;
UINTN Blocks;
UINTN TransferSize;
UINTN MaxTransfer;
UINT8 SenseData[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
BOOLEAN MediaChanged;
BlockSize = Device->Media.BlockSize;
MaxTransfer = 0xFFFF * BlockSize;
while (BufferSize > 0) {
TransferSize = (BufferSize < MaxTransfer) ? BufferSize : MaxTransfer;
Blocks = TransferSize / BlockSize;
if (Lba > 0xFFFFFFFF) {
//
// READ16 (0x88): 16-byte CDB
//
Cdb[0] = SCSI_OP_READ16;
Cdb[1] = 0;
Cdb[2] = (UINT8)(Lba >> 56);
Cdb[3] = (UINT8)(Lba >> 48);
Cdb[4] = (UINT8)(Lba >> 40);
Cdb[5] = (UINT8)(Lba >> 32);
Cdb[6] = (UINT8)(Lba >> 24);
Cdb[7] = (UINT8)(Lba >> 16);
Cdb[8] = (UINT8)(Lba >> 8);
Cdb[9] = (UINT8)(Lba);
Cdb[10] = (UINT8)(Blocks >> 24);
Cdb[11] = (UINT8)(Blocks >> 16);
Cdb[12] = (UINT8)(Blocks >> 8);
Cdb[13] = (UINT8)(Blocks);
Cdb[14] = 0;
Cdb[15] = 0;
Status = ScsiDiskExecuteScsiCommand (
Device->ScsiIo,
SCSI_TIMEOUT_1S,
Buffer,
SenseData,
&HostAdapterStatus,
&TargetStatus,
NULL,
(UINT32 *)&TransferSize,
Cdb,
16
);
} else {
//
// READ10 (0x28): 10-byte CDB
//
Cdb[0] = SCSI_OP_READ10;
Cdb[1] = 0;
Cdb[2] = (UINT8)(Lba >> 24);
Cdb[3] = (UINT8)(Lba >> 16);
Cdb[4] = (UINT8)(Lba >> 8);
Cdb[5] = (UINT8)(Lba);
Cdb[6] = 0;
Cdb[7] = (UINT8)(Blocks >> 8);
Cdb[8] = (UINT8)(Blocks);
Cdb[9] = 0;
Status = ScsiDiskExecuteScsiCommand (
Device->ScsiIo,
SCSI_TIMEOUT_1S,
Buffer,
SenseData,
&HostAdapterStatus,
&TargetStatus,
NULL,
(UINT32 *)&TransferSize,
Cdb,
10
);
}
if (EFI_ERROR (Status) ||
ScsiDiskIsHostAdapterError (HostAdapterStatus) ||
ScsiDiskIsTargetError (TargetStatus)) {
Status = ScsiDiskRetryCommand (
Device,
&MediaChanged,
&HostAdapterStatus,
SenseData,
SCSI_DISK_MAX_RETRY
);
if (EFI_ERROR (Status)) {
return Status;
}
continue;
}
Buffer = (UINT8 *)Buffer + TransferSize;
Lba += Blocks;
BufferSize -= TransferSize;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ScsiDiskWriteSectors (
IN SCSI_DISK_DEVICE *Device,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN VOID *Buffer,
IN UINTN BufferSize
)
{
EFI_STATUS Status;
UINT8 Cdb[16];
UINT32 BlockSize;
UINTN Blocks;
UINTN TransferSize;
UINTN MaxTransfer;
UINT8 SenseData[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
BOOLEAN MediaChanged;
BlockSize = Device->Media.BlockSize;
MaxTransfer = 0xFFFF * BlockSize;
while (BufferSize > 0) {
TransferSize = (BufferSize < MaxTransfer) ? BufferSize : MaxTransfer;
Blocks = TransferSize / BlockSize;
if (Lba > 0xFFFFFFFF) {
//
// WRITE16 (0x8A): 16-byte CDB
//
Cdb[0] = SCSI_OP_WRITE16;
Cdb[1] = 0;
Cdb[2] = (UINT8)(Lba >> 56);
Cdb[3] = (UINT8)(Lba >> 48);
Cdb[4] = (UINT8)(Lba >> 40);
Cdb[5] = (UINT8)(Lba >> 32);
Cdb[6] = (UINT8)(Lba >> 24);
Cdb[7] = (UINT8)(Lba >> 16);
Cdb[8] = (UINT8)(Lba >> 8);
Cdb[9] = (UINT8)(Lba);
Cdb[10] = (UINT8)(Blocks >> 24);
Cdb[11] = (UINT8)(Blocks >> 16);
Cdb[12] = (UINT8)(Blocks >> 8);
Cdb[13] = (UINT8)(Blocks);
Cdb[14] = 0;
Cdb[15] = 0;
Status = ScsiDiskExecuteScsiCommand (
Device->ScsiIo,
SCSI_TIMEOUT_1S,
Buffer,
SenseData,
&HostAdapterStatus,
&TargetStatus,
NULL,
(UINT32 *)&TransferSize,
Cdb,
16
);
} else {
//
// WRITE10 (0x2A): 10-byte CDB
//
Cdb[0] = SCSI_OP_WRITE10;
Cdb[1] = 0;
Cdb[2] = (UINT8)(Lba >> 24);
Cdb[3] = (UINT8)(Lba >> 16);
Cdb[4] = (UINT8)(Lba >> 8);
Cdb[5] = (UINT8)(Lba);
Cdb[6] = 0;
Cdb[7] = (UINT8)(Blocks >> 8);
Cdb[8] = (UINT8)(Blocks);
Cdb[9] = 0;
Status = ScsiDiskExecuteScsiCommand (
Device->ScsiIo,
SCSI_TIMEOUT_1S,
Buffer,
SenseData,
&HostAdapterStatus,
&TargetStatus,
NULL,
(UINT32 *)&TransferSize,
Cdb,
10
);
}
if (EFI_ERROR (Status) ||
ScsiDiskIsHostAdapterError (HostAdapterStatus) ||
ScsiDiskIsTargetError (TargetStatus)) {
Status = ScsiDiskRetryCommand (
Device,
&MediaChanged,
&HostAdapterStatus,
SenseData,
SCSI_DISK_MAX_RETRY
);
if (EFI_ERROR (Status)) {
return Status;
}
continue;
}
Buffer = (UINT8 *)Buffer + TransferSize;
Lba += Blocks;
BufferSize -= TransferSize;
}
return EFI_SUCCESS;
}
//
// Media Detection and Sense Processing
//
EFI_STATUS
ScsiDiskDetectMedia (
IN SCSI_DISK_DEVICE *Device,
IN BOOLEAN MustBeMediaPresent,
OUT BOOLEAN *MediaChanged
)
{
EFI_STATUS Status;
VOID *SenseData;
UINT8 SenseDataLength;
UINTN Result;
BOOLEAN RetrySupported;
*MediaChanged = FALSE;
//
// Step 1: TEST UNIT READY
//
Status = ScsiDiskTestUnitReady (Device, &RetrySupported, &SenseData, &SenseDataLength);
if (EFI_ERROR (Status)) {
//
// TUR failed -- parse sense to determine cause
//
if (ScsiDiskParseSenseData (Device, SenseData, SenseDataLength, &Result)) {
switch (Result) {
case SCSI_SENSE_IS_NO_MEDIA:
Device->MediaPresentFlag = 0;
Device->Media.MediaPresent = 0;
return EFI_NO_MEDIA;
case SCSI_SENSE_IS_MEDIA_CHANGE:
*MediaChanged = TRUE;
Device->MediaPresentFlag = 1;
Device->Media.MediaPresent = 1;
break;
case SCSI_SENSE_IS_RESET_BEFORE:
*MediaChanged = TRUE;
Device->MediaPresentFlag = 1;
Device->Media.MediaPresent = 1;
break;
default:
return EFI_DEVICE_ERROR;
}
}
}
//
// Step 2: If media appears present, read capacity
//
if (Device->MediaPresentFlag) {
Status = ScsiDiskReadCapacity (Device);
}
return Status;
}
EFI_STATUS
ScsiDiskInquiryDevice (
IN SCSI_DISK_DEVICE *Device,
OUT BOOLEAN *RetrySupported
)
{
EFI_STATUS Status;
UINT8 SenseData[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
UINT8 InquiryData[36];
UINT32 DataLength;
UINT8 VpdPage[VPD_MAX_PAGE_LENGTH];
UINT32 VpdLength;
UINTN Index;
//
// Standard INQUIRY (36 bytes)
//
DataLength = 36;
Status = ScsiDiskExecuteInquiry (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseData,
&HostAdapterStatus,
&TargetStatus,
InquiryData,
&DataLength
);
if (EFI_ERROR (Status)) {
return Status;
}
Device->InquiryDataType = InquiryData[0];
//
// Check if VPD pages are supported (byte 3, bit 4)
//
if (InquiryData[3] & 0x10) {
//
// Request VPD page list (page 0x00)
//
VpdLength = VPD_MAX_PAGE_LENGTH;
Status = ScsiDiskExecuteInquiry16 (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseData,
&HostAdapterStatus,
&TargetStatus,
VpdPage,
&VpdLength,
VPD_SUPPORTED_PAGES
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Search for page 0xB0 (Block Limits / thin provisioning)
//
for (Index = 4; Index < VpdLength; Index++) {
if (VpdPage[Index] == VPD_BLOCK_LIMITS) {
//
// Read VPD page 0xB0
//
VpdLength = VPD_BLOCK_LIMITS_LEN;
Status = ScsiDiskExecuteInquiry16 (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseData,
&HostAdapterStatus,
&TargetStatus,
VpdPage,
&VpdLength,
VPD_BLOCK_LIMITS
);
if (!EFI_ERROR (Status) &&
VpdPage[1] == VPD_BLOCK_LIMITS) {
Device->UnmapEraseData = 1;
Device->UnmapEraseData2 = 1;
Device->EraseBlockEnabled= 1;
}
break;
}
}
Device->InquiryVpdProcessed = 1;
}
return EFI_SUCCESS;
}
EFI_STATUS
ScsiDiskTestUnitReady (
IN SCSI_DISK_DEVICE *Device,
OUT BOOLEAN *RetrySupported,
OUT VOID **SenseData,
OUT UINT8 *SenseDataLength
)
{
EFI_STATUS Status;
UINT8 SenseBuf[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
UINTN RetryCount;
UINTN Result;
*RetrySupported = TRUE;
for (RetryCount = 0; RetryCount < SCSI_DISK_MAX_RETRY; RetryCount++) {
Status = ScsiDiskExecuteTestUnitReady (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseBuf,
&HostAdapterStatus,
&TargetStatus
);
if (!EFI_ERROR (Status)) {
*SenseData = NULL;
*SenseDataLength = 0;
return EFI_SUCCESS;
}
if (ScsiDiskIsHostAdapterError (HostAdapterStatus)) {
return EFI_DEVICE_ERROR;
}
if (ScsiDiskIsTargetError (TargetStatus)) {
*RetrySupported = FALSE;
continue;
}
//
// CHECK CONDITION -- request sense
//
Status = ScsiDiskRequestSense (Device, RetrySupported, SenseData, SenseDataLength);
if (EFI_ERROR (Status)) {
return Status;
}
if (ScsiDiskParseSenseData (Device, *SenseData, *SenseDataLength, &Result)) {
switch (Result) {
case SCSI_SENSE_IS_NO_MEDIA:
return EFI_NO_MEDIA;
case SCSI_SENSE_IS_MEDIA_CHANGE:
return EFI_MEDIA_CHANGED;
case SCSI_SENSE_IS_RESET_BEFORE:
continue;
default:
if (RetryCount >= SCSI_DISK_MAX_RETRY - 1) {
return EFI_DEVICE_ERROR;
}
continue;
}
}
}
return EFI_DEVICE_ERROR;
}
BOOLEAN
ScsiDiskParseSenseData (
IN SCSI_DISK_DEVICE *Device,
IN VOID *SenseData,
IN UINT8 SenseDataLength,
OUT UINTN *Result
)
{
UINT8 *Sense;
UINT8 SenseKey;
UINT8 Asc;
UINT8 Ascq;
if (SenseData == NULL || SenseDataLength < 18) {
return FALSE;
}
Sense = (UINT8 *)SenseData;
//
// Fixed format sense data:
// byte 0: Valid (bit 7) | Response code
// byte 2: Sense key (bits 0-3)
// byte 12: ASC
// byte 13: ASCQ
//
SenseKey = Sense[2] & 0x0F;
Asc = Sense[12];
Ascq = Sense[13];
//
// NOT READY + MEDIUM NOT PRESENT = no media
//
if (SenseKey == SCSI_SENSE_NOT_READY && Asc == SCSI_ASC_MEDIUM_NOT_PRESENT) {
*Result = SCSI_SENSE_IS_NO_MEDIA;
return TRUE;
}
//
// UNIT ATTENTION + MEDIUM CHANGED = media change
//
if (SenseKey == SCSI_SENSE_UNIT_ATTENTION && Asc == SCSI_ASC_MEDIUM_CHANGED) {
*Result = SCSI_SENSE_IS_MEDIA_CHANGE;
return TRUE;
}
//
// UNIT ATTENTION + RESET = bus reset detected
//
if (SenseKey == SCSI_SENSE_UNIT_ATTENTION && Asc == SCSI_ASC_UNIT_ATTENTION_RESET) {
*Result = SCSI_SENSE_IS_RESET_BEFORE;
return TRUE;
}
//
// Generic error
//
*Result = SCSI_SENSE_IS_ERROR;
return TRUE;
}
EFI_STATUS
ScsiDiskRequestSense (
IN SCSI_DISK_DEVICE *Device,
OUT BOOLEAN *RetrySupported,
OUT VOID **SenseData,
OUT UINT8 *SenseDataLength
)
{
EFI_STATUS Status;
UINT8 SenseBuf[SCSI_DESC_SIZE];
UINT32 DataLength;
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
DataLength = SCSI_DESC_SIZE;
Status = ScsiDiskExecuteRequestSense (
Device->ScsiIo,
SCSI_TIMEOUT_10MS,
SenseBuf,
&HostAdapterStatus,
&TargetStatus
);
if (!EFI_ERROR (Status) &&
HostAdapterStatus == 0 &&
TargetStatus == 0) {
*SenseData = SenseBuf;
*SenseDataLength = SCSI_DESC_SIZE;
return EFI_SUCCESS;
}
*SenseData = NULL;
*SenseDataLength = 0;
return EFI_DEVICE_ERROR;
}
STATIC
EFI_STATUS
ScsiDiskReadCapacity (
IN SCSI_DISK_DEVICE *Device
)
{
EFI_STATUS Status;
UINT8 SenseData[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
UINT8 CapacityData[8];
UINT8 CapacityData16[32];
UINT32 DataLength;
//
// Try READ CAPACITY (10) first
//
DataLength = 8;
Status = ScsiDiskExecuteReadCapacity10 (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseData,
&HostAdapterStatus,
&TargetStatus,
CapacityData,
&DataLength
);
if (!EFI_ERROR (Status)) {
ScsiDiskParseCapacityData (Device, CapacityData, FALSE);
return EFI_SUCCESS;
}
//
// Fall back to READ CAPACITY (16)
//
DataLength = 32;
Status = ScsiDiskExecuteReadCapacity16 (
Device->ScsiIo,
SCSI_TIMEOUT_300MS,
SenseData,
&HostAdapterStatus,
&TargetStatus,
CapacityData16,
&DataLength
);
if (!EFI_ERROR (Status)) {
ScsiDiskParseCapacityData (Device, CapacityData16, TRUE);
return EFI_SUCCESS;
}
return Status;
}
STATIC
VOID
ScsiDiskParseCapacityData (
IN SCSI_DISK_DEVICE *Device,
IN UINT8 *Data,
IN BOOLEAN IsCapacity16
)
{
UINT32 BlockSize;
UINT64 LastBlock;
if (IsCapacity16) {
//
// READ CAPACITY (16): bytes 0-7 = returned LBA, bytes 8-11 = block size
//
LastBlock = ((UINT64)Data[0] << 56) |
((UINT64)Data[1] << 48) |
((UINT64)Data[2] << 40) |
((UINT64)Data[3] << 32) |
((UINT64)Data[4] << 24) |
((UINT64)Data[5] << 16) |
((UINT64)Data[6] << 8) |
((UINT64)Data[7]);
BlockSize = ((UINT32)Data[8] << 24) |
((UINT32)Data[9] << 16) |
((UINT32)Data[10] << 8) |
((UINT32)Data[11]);
} else {
//
// READ CAPACITY (10): bytes 0-3 = returned LBA, bytes 4-7 = block size
//
LastBlock = ((UINT32)Data[0] << 24) |
((UINT32)Data[1] << 16) |
((UINT32)Data[2] << 8) |
((UINT32)Data[3]);
BlockSize = ((UINT32)Data[4] << 24) |
((UINT32)Data[5] << 16) |
((UINT32)Data[6] << 8) |
((UINT32)Data[7]);
}
Device->Media.BlockSize = BlockSize;
Device->Media.LastBlock = LastBlock;
Device->Media.MediaId++;
}
//
// Error Handling
//
BOOLEAN
ScsiDiskIsHostAdapterError (
IN UINT8 Status
)
{
switch (Status) {
case 0x00: // EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK
case 0x02: // EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_FAIL
return FALSE;
default:
return TRUE;
}
}
BOOLEAN
ScsiDiskIsTargetError (
IN UINT8 Status
)
{
switch (Status) {
case 0x00: // EFI_EXT_SCSI_STATUS_TARGET_GOOD
case 0x02: // EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION
return FALSE;
default:
return TRUE;
}
}
EFI_STATUS
ScsiDiskRetryCommand (
IN SCSI_DISK_DEVICE *Device,
OUT BOOLEAN *MediaChanged,
IN UINT8 *HostAdapterStatus,
IN UINT8 *SenseData,
IN UINT8 RetryCount
)
{
UINTN Result;
UINTN RetryIndex;
for (RetryIndex = 0; RetryIndex < RetryCount; RetryIndex++) {
if (ScsiDiskIsHostAdapterError (*HostAdapterStatus)) {
Device->ScsiIo->ResetBus (Device->ScsiIo);
}
if (ScsiDiskIsTargetError (*HostAdapterStatus)) {
Device->ScsiIo->ResetDevice (Device->ScsiIo);
*MediaChanged = TRUE;
}
if (ScsiDiskParseSenseData (Device, SenseData, SCSI_DESC_SIZE, &Result)) {
if (Result == SCSI_SENSE_IS_MEDIA_CHANGE) {
*MediaChanged = TRUE;
return EFI_MEDIA_CHANGED;
}
if (Result == SCSI_SENSE_IS_NO_MEDIA) {
return EFI_NO_MEDIA;
}
}
return EFI_SUCCESS;
}
return EFI_DEVICE_ERROR;
}
//
// UNMAP / Erase Block
//
EFI_STATUS
ScsiDiskUnmap (
IN SCSI_DISK_DEVICE *Device,
IN EFI_LBA LBA,
IN UINTN RemainingBytes
)
{
EFI_STATUS Status;
UINT8 Cdb[10];
UINT8 SenseData[SCSI_DESC_SIZE];
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
UINT8 ParamList[24];
UINT32 ParamListSize;
UINT32 BlockSize;
UINT32 BlockCount;
BOOLEAN MediaChanged;
BlockSize = Device->Media.BlockSize;
BlockCount = (UINT32)(RemainingBytes / BlockSize);
//
// Build UNMAP parameter list
//
ParamListSize = 24;
//
// Header (8 bytes)
//
ParamList[0] = 0;
ParamList[1] = 0;
ParamList[2] = 0;
ParamList[3] = 16; // Data length
ParamList[4] = 0;
ParamList[5] = 0;
ParamList[6] = 0;
ParamList[7] = 12; // Block descriptor data length
//
// Reserved
//
ParamList[8] = 0;
ParamList[9] = 0;
ParamList[10] = 0;
ParamList[11] = 0;
//
// Block descriptor (16 bytes): LBA + block count
//
ParamList[12] = (UINT8)(LBA >> 56);
ParamList[13] = (UINT8)(LBA >> 48);
ParamList[14] = (UINT8)(LBA >> 40);
ParamList[15] = (UINT8)(LBA >> 32);
ParamList[16] = (UINT8)(LBA >> 24);
ParamList[17] = (UINT8)(LBA >> 16);
ParamList[18] = (UINT8)(LBA >> 8);
ParamList[19] = (UINT8)(LBA);
ParamList[20] = (UINT8)(BlockCount >> 24);
ParamList[21] = (UINT8)(BlockCount >> 16);
ParamList[22] = (UINT8)(BlockCount >> 8);
ParamList[23] = (UINT8)(BlockCount);
//
// Build UNMAP CDB (10 bytes)
//
Cdb[0] = SCSI_OP_UNMAP;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = 0;
Cdb[5] = 0;
Cdb[6] = 0;
Cdb[7] = 0;
Cdb[8] = ParamListSize;
Cdb[9] = 0;
Status = ScsiDiskExecuteScsiCommand (
Device->ScsiIo,
SCSI_TIMEOUT_1S,
NULL,
SenseData,
&HostAdapterStatus,
&TargetStatus,
ParamList,
&ParamListSize,
Cdb,
10
);
if (EFI_ERROR (Status) ||
ScsiDiskIsHostAdapterError (HostAdapterStatus) ||
ScsiDiskIsTargetError (TargetStatus)) {
Status = ScsiDiskRetryCommand (Device, &MediaChanged, &HostAdapterStatus, SenseData, SCSI_DISK_MAX_RETRY);
}
return Status;
}
EFI_STATUS
EFIAPI
ScsiDiskEraseBlocks (
IN EFI_ERASE_BLOCK_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA LBA,
IN UINTN *RemainingBytes
)
{
SCSI_DISK_DEVICE *Device;
EFI_STATUS Status;
Device = CR (This, SCSI_DISK_DEVICE, EraseBlock, SCSI_DISK_DEVICE_SIGNATURE);
if (RemainingBytes == NULL || *RemainingBytes == 0) {
return EFI_INVALID_PARAMETER;
}
if (!Device->EraseBlockEnabled) {
return EFI_UNSUPPORTED;
}
Status = ScsiDiskUnmap (Device, LBA, *RemainingBytes);
if (!EFI_ERROR (Status)) {
*RemainingBytes = 0;
}
return Status;
}
//
// Async UNMAP Notification
//
VOID
EFIAPI
ScsiDiskAsyncUnmapNotification (
IN EFI_EVENT Event,
IN VOID *Context
)
{
SCSI_DISK_ASYNC_UNMAP_NOTIFICATION *Notification;
Notification = (SCSI_DISK_ASYNC_UNMAP_NOTIFICATION *)Context;
if (Notification == NULL) {
return;
}
//
// Signal completion by signaling the event
//
gBS->SignalEvent (Notification->Event);
}
VOID
EFIAPI
ScsiDiskAsyncUnmapTimer (
IN EFI_EVENT Event,
IN VOID *Context
)
{
SCSI_DISK_DEVICE *Device;
LIST_ENTRY *Entry;
SCSI_DISK_ASYNC_UNMAP_NOTIFICATION *Notification;
Device = (SCSI_DISK_DEVICE *)Context;
if (Device == NULL) {
return;
}
//
// Process pending notifications
//
while (!ScsiDiskIsListEmpty (&Device->AsyncUnmapQueue)) {
Entry = Device->AsyncUnmapQueue.ForwardLink;
Notification = BASE_CR (Entry, SCSI_DISK_ASYNC_UNMAP_NOTIFICATION, Link);
ScsiDiskRemoveEntryList (&Notification->Link);
if (Notification->Buffer != NULL) {
gBS->FreePool (Notification->Buffer);
}
gBS->CloseEvent (Notification->Event);
gBS->FreePool (Notification);
}
}
//
// Device Node Lifecycle
//
EFI_STATUS
ScsiDiskCreateDeviceNode (
OUT SCSI_DISK_DEVICE **Device,
IN EFI_SCSI_IO_SCSI_REQUEST *Request
)
{
SCSI_DISK_DEVICE *NewDevice;
NewDevice = (SCSI_DISK_DEVICE *)gBS->AllocateZeroPool (SCSI_DISK_DEVICE_SIZE);
if (NewDevice == NULL) {
return EFI_OUT_OF_RESOURCES;
}
NewDevice->Signature = SCSI_DISK_DEVICE_SIGNATURE;
ScsiDiskInitializeListHead (&NewDevice->AsyncUnmapQueue);
*Device = NewDevice;
return EFI_SUCCESS;
}
VOID
ScsiDiskFreeDeviceMemory (
IN SCSI_DISK_DEVICE *Device
)
{
LIST_ENTRY *Entry;
LIST_ENTRY *NextEntry;
SCSI_DISK_ASYNC_UNMAP_NOTIFICATION *Notification;
if (Device == NULL) {
return;
}
//
// Cancel pending async UNMAP notifications
//
Entry = Device->AsyncUnmapQueue.ForwardLink;
while (Entry != &Device->AsyncUnmapQueue) {
NextEntry = Entry->ForwardLink;
Notification = BASE_CR (Entry, SCSI_DISK_ASYNC_UNMAP_NOTIFICATION, Link);
gBS->CloseEvent (Notification->Event);
if (Notification->Buffer != NULL) {
gBS->FreePool (Notification->Buffer);
}
gBS->FreePool (Notification);
Entry = NextEntry;
}
gBS->FreePool (Device);
}
EFI_STATUS
ScsiDiskRegisterExitBootServices (
IN SCSI_DISK_DEVICE *Device,
IN EFI_HANDLE ControllerHandle
)
{
EFI_STATUS Status;
Status = gBS->CreateEvent (
EVT_SIGNAL_EXIT_BOOT_SERVICES,
TPL_CALLBACK,
ScsiDiskExitBootServicesCallback,
Device,
&Device->ControllerHandle
);
return Status;
}
VOID
EFIAPI
ScsiDiskExitBootServicesCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
SCSI_DISK_DEVICE *Device;
Device = (SCSI_DISK_DEVICE *)Context;
if (Device == NULL) {
return;
}
//
// Process pending async queue before OS takes over
//
while (!ScsiDiskIsListEmpty (&Device->AsyncUnmapQueue)) {
ScsiDiskAsyncUnmapTimer (Event, Device);
}
}
//
// Device Capability Checks
//
BOOLEAN
ScsiDiskIsDeviceRemovable (
IN SCSI_DISK_DEVICE *Device,
IN EFI_HANDLE ControllerHandle
)
{
//
// Check removable bit in INQUIRY data byte 1, bit 7
//
return (Device->InquiryDataType & 0x80) ? TRUE : FALSE;
}
BOOLEAN
ScsiDiskIsNeedEraseBlock (
IN SCSI_DISK_DEVICE *Device,
IN EFI_LBA LBA,
IN UINT32 *RemainingBytes
)
{
return Device->EraseBlockEnabled ? TRUE : FALSE;
}
BOOLEAN
ScsiDiskIsEraseBlockProtocolInstalled (
IN EFI_HANDLE ControllerHandle
)
{
EFI_STATUS Status;
VOID *Interface;
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiBlockIoProtocolGuid,
&Interface,
gImageHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
return (BOOLEAN)(!EFI_ERROR (Status));
}
//
// Component Name Protocol
//
EFI_STATUS
EFIAPI
ScsiDiskComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
if (Language == NULL || DriverName == NULL) {
return EFI_INVALID_PARAMETER;
}
*DriverName = L"Scsi Disk Driver";
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
ScsiDiskComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
if (Language == NULL || ControllerName == NULL) {
return EFI_INVALID_PARAMETER;
}
*ControllerName = L"SCSI Disk Device";
return EFI_SUCCESS;
}
//
// SCSI Command Execution Helpers
//
EFI_STATUS
ScsiDiskExecuteScsiCommand (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
IN VOID *DataBuffer,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus,
IN VOID *Data,
IN OUT UINT32 *DataLength,
IN UINT8 *Cdb,
IN UINT8 CdbLength
)
{
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
CdbLength,
NULL,
0,
DataLength,
DataBuffer,
*DataLength,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteTestUnitReady (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus
)
{
UINT8 Cdb[6];
UINT32 DataLength;
Cdb[0] = SCSI_OP_TEST_UNIT_READY;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = 0;
Cdb[5] = 0;
DataLength = 0;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
6,
NULL,
0,
&DataLength,
NULL,
0,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteRequestSense (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus
)
{
UINT8 Cdb[6];
UINT32 DataLength;
Cdb[0] = SCSI_OP_REQUEST_SENSE;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = SCSI_DESC_SIZE;
Cdb[5] = 0;
DataLength = SCSI_DESC_SIZE;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
6,
NULL,
0,
&DataLength,
SenseData,
SCSI_DESC_SIZE,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteInquiry (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus,
OUT VOID *DataBuffer,
IN OUT UINT32 *DataLength
)
{
UINT8 Cdb[6];
Cdb[0] = SCSI_OP_INQUIRY;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = 36;
Cdb[5] = 0;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
6,
NULL,
0,
DataLength,
DataBuffer,
*DataLength,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteInquiry16 (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus,
OUT VOID *DataBuffer,
IN OUT UINT32 *DataLength,
IN UINT8 PageCode
)
{
UINT8 Cdb[16];
Cdb[0] = SCSI_OP_INQUIRY;
Cdb[1] = 0x01; // EVPD = 1
Cdb[2] = PageCode;
Cdb[3] = 0;
Cdb[4] = 0;
Cdb[5] = 0;
Cdb[6] = 0;
Cdb[7] = 0;
Cdb[8] = 0;
Cdb[9] = 0;
Cdb[10] = 0;
Cdb[11] = 0;
Cdb[12] = 0;
Cdb[13] = 0;
Cdb[14] = 0;
Cdb[15] = 0;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
16,
NULL,
0,
DataLength,
DataBuffer,
*DataLength,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteReadCapacity10 (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus,
OUT VOID *DataBuffer,
IN OUT UINT32 *DataLength
)
{
UINT8 Cdb[10];
Cdb[0] = SCSI_OP_READ_CAPACITY10;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = 0;
Cdb[5] = 0;
Cdb[6] = 0;
Cdb[7] = 0;
Cdb[8] = 0;
Cdb[9] = 0;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
10,
NULL,
0,
DataLength,
DataBuffer,
*DataLength,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
EFI_STATUS
ScsiDiskExecuteReadCapacity16 (
IN EFI_SCSI_IO_PROTOCOL *ScsiIo,
IN UINT64 Timeout,
OUT UINT8 *SenseData,
OUT UINT8 *HostAdapterStatus,
OUT UINT8 *TargetStatus,
OUT VOID *DataBuffer,
IN OUT UINT32 *DataLength
)
{
UINT8 Cdb[16];
Cdb[0] = SCSI_OP_READ_CAPACITY16;
Cdb[1] = 0;
Cdb[2] = 0;
Cdb[3] = 0;
Cdb[4] = 0;
Cdb[5] = 0;
Cdb[6] = 0;
Cdb[7] = 0;
Cdb[8] = 0;
Cdb[9] = 0;
Cdb[10] = 0;
Cdb[11] = 0;
Cdb[12] = 0;
Cdb[13] = 0;
Cdb[14] = 0;
Cdb[15] = 0;
return ScsiIo->ExecScsiCommand (
ScsiIo,
Cdb,
16,
NULL,
0,
DataLength,
DataBuffer,
*DataLength,
Timeout,
HostAdapterStatus,
TargetStatus
);
}
//
// Library Import Wrappers (statically linked replacements)
//
UINTN
ScsiDiskIsListEmpty (
IN LIST_ENTRY *ListHead
)
{
return (BOOLEAN)(ListHead->ForwardLink == ListHead);
}
VOID
ScsiDiskInitializeListHead (
IN LIST_ENTRY *ListHead
)
{
ListHead->ForwardLink = ListHead;
ListHead->BackLink = ListHead;
}
VOID
ScsiDiskInsertTailList (
IN LIST_ENTRY *ListHead,
IN LIST_ENTRY *Entry
)
{
Entry->ForwardLink = ListHead;
Entry->BackLink = ListHead->BackLink;
ListHead->BackLink->ForwardLink = Entry;
ListHead->BackLink = Entry;
}
VOID
ScsiDiskRemoveEntryList (
IN LIST_ENTRY *Entry
)
{
Entry->BackLink->ForwardLink = Entry->ForwardLink;
Entry->ForwardLink->BackLink = Entry->BackLink;
}
UINT16
ScsiDiskSwapBytes16 (
IN UINT16 Value
)
{
return (UINT16)((Value >> 8) | (Value << 8));
}
UINT32
ScsiDiskSwapBytes32 (
IN UINT32 Value
)
{
return ((Value >> 24) & 0x000000FF) |
((Value >> 8) & 0x0000FF00) |
((Value << 8) & 0x00FF0000) |
((Value << 24) & 0xFF000000);
}
UINT64
ScsiDiskSwapBytes64 (
IN UINT64 Value
)
{
return ((UINT64)ScsiDiskSwapBytes32 ((UINT32)Value) << 32) |
(UINT64)ScsiDiskSwapBytes32 ((UINT32)(Value >> 32));
}
UINT64
ScsiDiskReadUnaligned64 (
IN VOID *Buffer
)
{
UINT64 Value;
gBS->CopyMem (&Value, Buffer, sizeof (Value));
return Value;
}
UINTN
ScsiDiskGetAlignmentFromMask (
IN UINTN AlignmentMask
)
{
UINTN Alignment;
if (AlignmentMask == 0) {
return 0;
}
for (Alignment = 1; !(AlignmentMask & Alignment); Alignment <<= 1) {
}
return Alignment;
}
BOOLEAN
ScsiDiskIsAlignedPointer (
IN VOID *Buffer,
IN UINTN Alignment
)
{
return (((UINTN)Buffer & (Alignment - 1)) == 0) ? TRUE : FALSE;
}
VOID *
ScsiDiskAllocatePool (
IN UINTN AllocationSize
)
{
VOID *Buffer;
gBS->AllocatePool (EfiBootServicesData, AllocationSize, &Buffer);
return Buffer;
}
VOID *
ScsiDiskAllocateZeroPool (
IN UINTN AllocationSize
)
{
VOID *Buffer;
Buffer = ScsiDiskAllocatePool (AllocationSize);
if (Buffer != NULL) {
gBS->SetMem (Buffer, AllocationSize, 0);
}
return Buffer;
}
VOID
ScsiDiskFreePool (
IN VOID *Buffer
)
{
if (Buffer != NULL) {
gBS->FreePool (Buffer);
}
}
VOID
ScsiDiskSafeFreePoolWithCheck (
VOID
)
{
//
// Placeholder for safe-free logic
//
}