2020-08-29 03:06:22 +00:00
/*++
Copyright ( C ) Microsoft Corporation , 1991 - 2010
Module Name :
utils . c
Abstract :
SCSI class driver routines
Environment :
kernel mode only
Notes :
Revision History :
- - */
# include "classp.h"
# include "debug.h"
# include <ntiologc.h>
# ifdef DEBUG_USE_WPP
# include "utils.tmh"
# endif
//
// Constant value used in firmware upgrade process.
//
# define FIRMWARE_ACTIVATE_TIMEOUT_VALUE 30
# ifdef ALLOC_PRAGMA
# pragma alloc_text(PAGE, ClassGetDeviceParameter)
# pragma alloc_text(PAGE, ClassScanForSpecial)
# pragma alloc_text(PAGE, ClassSetDeviceParameter)
# pragma alloc_text(PAGE, ClasspMyStringMatches)
# pragma alloc_text(PAGE, ClasspDeviceCopyOffloadProperty)
# pragma alloc_text(PAGE, ClasspValidateOffloadSupported)
# pragma alloc_text(PAGE, ClasspValidateOffloadInputParameters)
# endif
// custom string match -- careful!
BOOLEAN ClasspMyStringMatches ( _In_opt_z_ PCHAR StringToMatch , _In_z_ PCHAR TargetString )
{
ULONG length ; // strlen returns an int, not size_t (!)
PAGED_CODE ( ) ;
NT_ASSERT ( TargetString ) ;
// if no match requested, return TRUE
if ( StringToMatch = = NULL ) {
return TRUE ;
}
// cache the string length for efficiency
length = ( ULONG ) strlen ( StringToMatch ) ;
// ZERO-length strings may only match zero-length strings
if ( length = = 0 ) {
return ( strlen ( TargetString ) = = 0 ) ;
}
// strncmp returns zero if the strings match
return ( strncmp ( StringToMatch , TargetString , length ) = = 0 ) ;
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassGetDeviceParameter (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_opt_ PWSTR SubkeyName ,
_In_ PWSTR ParameterName ,
_Inout_ PULONG ParameterValue // also default value
)
{
NTSTATUS status ;
RTL_QUERY_REGISTRY_TABLE queryTable [ 2 ] = { 0 } ;
HANDLE deviceParameterHandle = NULL ;
HANDLE deviceSubkeyHandle = NULL ;
ULONG defaultParameterValue ;
PAGED_CODE ( ) ;
//
// open the given parameter
//
status = IoOpenDeviceRegistryKey ( FdoExtension - > LowerPdo ,
PLUGPLAY_REGKEY_DEVICE ,
KEY_READ ,
& deviceParameterHandle ) ;
if ( NT_SUCCESS ( status ) & & ( SubkeyName ! = NULL ) ) {
UNICODE_STRING subkeyName ;
OBJECT_ATTRIBUTES objectAttributes = { 0 } ;
RtlInitUnicodeString ( & subkeyName , SubkeyName ) ;
InitializeObjectAttributes ( & objectAttributes ,
& subkeyName ,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE ,
deviceParameterHandle ,
NULL ) ;
status = ZwOpenKey ( & deviceSubkeyHandle ,
KEY_READ ,
& objectAttributes ) ;
if ( ! NT_SUCCESS ( status ) ) {
ZwClose ( deviceParameterHandle ) ;
}
}
if ( NT_SUCCESS ( status ) ) {
defaultParameterValue = * ParameterValue ;
queryTable - > Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK ;
queryTable - > Name = ParameterName ;
queryTable - > EntryContext = ParameterValue ;
queryTable - > DefaultType = ( REG_DWORD < < RTL_QUERY_REGISTRY_TYPECHECK_SHIFT ) | REG_NONE ;
queryTable - > DefaultData = NULL ;
queryTable - > DefaultLength = 0 ;
status = RtlQueryRegistryValues ( RTL_REGISTRY_HANDLE ,
( PWSTR ) ( SubkeyName ?
deviceSubkeyHandle :
deviceParameterHandle ) ,
queryTable ,
NULL ,
NULL ) ;
if ( ! NT_SUCCESS ( status ) ) {
* ParameterValue = defaultParameterValue ; // use default value
}
//
// close what we open
//
if ( SubkeyName ) {
ZwClose ( deviceSubkeyHandle ) ;
}
ZwClose ( deviceParameterHandle ) ;
}
if ( ! NT_SUCCESS ( status ) ) {
//
// Windows 2000 SP3 uses the driver-specific key, so look in there
//
status = IoOpenDeviceRegistryKey ( FdoExtension - > LowerPdo ,
PLUGPLAY_REGKEY_DRIVER ,
KEY_READ ,
& deviceParameterHandle ) ;
if ( NT_SUCCESS ( status ) & & ( SubkeyName ! = NULL ) ) {
UNICODE_STRING subkeyName ;
OBJECT_ATTRIBUTES objectAttributes = { 0 } ;
RtlInitUnicodeString ( & subkeyName , SubkeyName ) ;
InitializeObjectAttributes ( & objectAttributes ,
& subkeyName ,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE ,
deviceParameterHandle ,
NULL ) ;
status = ZwOpenKey ( & deviceSubkeyHandle , KEY_READ , & objectAttributes ) ;
if ( ! NT_SUCCESS ( status ) ) {
ZwClose ( deviceParameterHandle ) ;
}
}
if ( NT_SUCCESS ( status ) ) {
defaultParameterValue = * ParameterValue ;
queryTable - > Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_TYPECHECK ;
queryTable - > Name = ParameterName ;
queryTable - > EntryContext = ParameterValue ;
queryTable - > DefaultType = ( REG_DWORD < < RTL_QUERY_REGISTRY_TYPECHECK_SHIFT ) | REG_NONE ;
queryTable - > DefaultData = NULL ;
queryTable - > DefaultLength = 0 ;
status = RtlQueryRegistryValues ( RTL_REGISTRY_HANDLE ,
( PWSTR ) ( SubkeyName ?
deviceSubkeyHandle :
deviceParameterHandle ) ,
queryTable ,
NULL ,
NULL ) ;
if ( NT_SUCCESS ( status ) ) {
//
// Migrate the value over to the device-specific key
//
ClassSetDeviceParameter ( FdoExtension , SubkeyName , ParameterName , * ParameterValue ) ;
} else {
//
// Use the default value
//
* ParameterValue = defaultParameterValue ;
}
if ( SubkeyName ) {
ZwClose ( deviceSubkeyHandle ) ;
}
ZwClose ( deviceParameterHandle ) ;
}
}
return ;
} // end ClassGetDeviceParameter()
_IRQL_requires_max_ ( PASSIVE_LEVEL )
NTSTATUS
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassSetDeviceParameter (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_opt_ PWSTR SubkeyName ,
_In_ PWSTR ParameterName ,
_In_ ULONG ParameterValue )
{
NTSTATUS status ;
HANDLE deviceParameterHandle = NULL ;
HANDLE deviceSubkeyHandle = NULL ;
PAGED_CODE ( ) ;
//
// open the given parameter
//
status = IoOpenDeviceRegistryKey ( FdoExtension - > LowerPdo ,
PLUGPLAY_REGKEY_DEVICE ,
KEY_READ | KEY_WRITE ,
& deviceParameterHandle ) ;
if ( NT_SUCCESS ( status ) & & ( SubkeyName ! = NULL ) ) {
UNICODE_STRING subkeyName ;
OBJECT_ATTRIBUTES objectAttributes ;
RtlInitUnicodeString ( & subkeyName , SubkeyName ) ;
InitializeObjectAttributes ( & objectAttributes ,
& subkeyName ,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE ,
deviceParameterHandle ,
NULL ) ;
status = ZwCreateKey ( & deviceSubkeyHandle ,
KEY_READ | KEY_WRITE ,
& objectAttributes ,
0 , NULL , 0 , NULL ) ;
if ( ! NT_SUCCESS ( status ) ) {
ZwClose ( deviceParameterHandle ) ;
}
}
if ( NT_SUCCESS ( status ) ) {
status = RtlWriteRegistryValue (
RTL_REGISTRY_HANDLE ,
( PWSTR ) ( SubkeyName ?
deviceSubkeyHandle :
deviceParameterHandle ) ,
ParameterName ,
REG_DWORD ,
& ParameterValue ,
sizeof ( ULONG ) ) ;
//
// close what we open
//
if ( SubkeyName ) {
ZwClose ( deviceSubkeyHandle ) ;
}
ZwClose ( deviceParameterHandle ) ;
}
return status ;
} // end ClassSetDeviceParameter()
/*
* ClassScanForSpecial
*
* This routine was written to simplify scanning for special
* hardware based upon id strings . it does not check the registry .
*/
_IRQL_requires_max_ ( PASSIVE_LEVEL )
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassScanForSpecial (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_ CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList [ ] ,
_In_ PCLASS_SCAN_FOR_SPECIAL_HANDLER Function )
{
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor ;
PUCHAR vendorId ;
PUCHAR productId ;
PUCHAR productRevision ;
UCHAR nullString [ ] = " " ;
PAGED_CODE ( ) ;
NT_ASSERT ( DeviceList ) ;
NT_ASSERT ( Function ) ;
deviceDescriptor = FdoExtension - > DeviceDescriptor ;
if ( DeviceList = = NULL ) {
return ;
}
if ( Function = = NULL ) {
return ;
}
//
// SCSI sets offsets to -1, ATAPI sets to 0. check for both.
//
if ( deviceDescriptor - > VendorIdOffset ! = 0 & &
deviceDescriptor - > VendorIdOffset ! = - 1 ) {
vendorId = ( ( PUCHAR ) deviceDescriptor ) ;
vendorId + = deviceDescriptor - > VendorIdOffset ;
} else {
vendorId = nullString ;
}
if ( deviceDescriptor - > ProductIdOffset ! = 0 & &
deviceDescriptor - > ProductIdOffset ! = - 1 ) {
productId = ( ( PUCHAR ) deviceDescriptor ) ;
productId + = deviceDescriptor - > ProductIdOffset ;
} else {
productId = nullString ;
}
if ( deviceDescriptor - > ProductRevisionOffset ! = 0 & &
deviceDescriptor - > ProductRevisionOffset ! = - 1 ) {
productRevision = ( ( PUCHAR ) deviceDescriptor ) ;
productRevision + = deviceDescriptor - > ProductRevisionOffset ;
} else {
productRevision = nullString ;
}
//
// loop while the device list is valid (not null-filled)
//
for ( ; ( DeviceList - > VendorId ! = NULL | |
DeviceList - > ProductId ! = NULL | |
DeviceList - > ProductRevision ! = NULL ) ; DeviceList + + ) {
if ( ClasspMyStringMatches ( DeviceList - > VendorId , ( PCHAR ) vendorId ) & &
ClasspMyStringMatches ( DeviceList - > ProductId , ( PCHAR ) productId ) & &
ClasspMyStringMatches ( DeviceList - > ProductRevision , ( PCHAR ) productRevision )
) {
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_INIT , " ClasspScanForSpecialByInquiry: Found matching "
" controller Ven: %s Prod: %s Rev: %s \n " ,
( PCSZ ) vendorId , ( PCSZ ) productId , ( PCSZ ) productRevision ) ) ;
//
// pass the context to the call back routine and exit
//
( Function ) ( FdoExtension , DeviceList - > Data ) ;
//
// for CHK builds, try to prevent wierd stacks by having a debug
// print here. it's a hack, but i know of no other way to prevent
// the stack from being wrong.
//
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_INIT , " ClasspScanForSpecialByInquiry: "
" completed callback \n " ) ) ;
return ;
} // else the strings did not match
} // none of the devices matched.
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_INIT , " ClasspScanForSpecialByInquiry: no match found for %p \n " ,
FdoExtension - > DeviceObject ) ) ;
return ;
} // end ClasspScanForSpecialByInquiry()
//
// In order to provide better performance without the need to reboot,
// we need to implement a self-adjusting method to set and clear the
// srb flags based upon current performance.
//
// whenever there is an error, immediately grab the spin lock. the
// MP perf hit here is acceptable, since we're in an error path. this
// is also neccessary because we are guaranteed to be modifying the
// SRB flags here, setting SuccessfulIO to zero, and incrementing the
// actual error count (which is always done within this spinlock).
//
// whenever there is no error, increment a counter. if there have been
// errors on the device, and we've enabled dynamic perf, *and* we've
// just crossed the perf threshhold, then grab the spin lock and
// double check that the threshhold has, indeed been hit(*). then
// decrement the error count, and if it's dropped sufficiently, undo
// some of the safety changes made in the SRB flags due to the errors.
//
// * this works in all cases. even if lots of ios occur after the
// previous guy went in and cleared the successfulio counter, that
// just means that we've hit the threshhold again, and so it's proper
// to run the inner loop again.
//
VOID
ClasspPerfIncrementErrorCount (
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension - > PrivateFdoData ;
KIRQL oldIrql ;
ULONG errors ;
KeAcquireSpinLock ( & fdoData - > SpinLock , & oldIrql ) ;
fdoData - > Perf . SuccessfulIO = 0 ; // implicit interlock
errors = InterlockedIncrement ( ( volatile LONG * ) & FdoExtension - > ErrorCount ) ;
if ( ! fdoData - > DisableThrottling ) {
if ( errors > = CLASS_ERROR_LEVEL_1 ) {
//
// If the error count has exceeded the error limit, then disable
// any tagged queuing, multiple requests per lu queueing
// and sychronous data transfers.
//
// Clearing the no queue freeze flag prevents the port driver
// from sending multiple requests per logical unit.
//
CLEAR_FLAG ( FdoExtension - > SrbFlags , SRB_FLAGS_NO_QUEUE_FREEZE ) ;
CLEAR_FLAG ( FdoExtension - > SrbFlags , SRB_FLAGS_QUEUE_ACTION_ENABLE ) ;
SET_FLAG ( FdoExtension - > SrbFlags , SRB_FLAGS_DISABLE_SYNCH_TRANSFER ) ;
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_GENERAL , " ClasspPerfIncrementErrorCount: "
" Too many errors; disabling tagged queuing and "
" synchronous data tranfers. \n " ) ) ;
}
if ( errors > = CLASS_ERROR_LEVEL_2 ) {
//
// If a second threshold is reached, disable disconnects.
//
SET_FLAG ( FdoExtension - > SrbFlags , SRB_FLAGS_DISABLE_DISCONNECT ) ;
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_GENERAL , " ClasspPerfIncrementErrorCount: "
" Too many errors; disabling disconnects. \n " ) ) ;
}
}
KeReleaseSpinLock ( & fdoData - > SpinLock , oldIrql ) ;
return ;
}
VOID
ClasspPerfIncrementSuccessfulIo (
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension - > PrivateFdoData ;
KIRQL oldIrql ;
ULONG errors ;
ULONG succeeded = 0 ;
//
// don't take a hit from the interlocked op unless we're in
// a degraded state and we've got a threshold to hit.
//
if ( FdoExtension - > ErrorCount = = 0 ) {
return ;
}
if ( fdoData - > Perf . ReEnableThreshhold = = 0 ) {
return ;
}
succeeded = InterlockedIncrement ( ( volatile LONG * ) & fdoData - > Perf . SuccessfulIO ) ;
if ( succeeded < fdoData - > Perf . ReEnableThreshhold ) {
return ;
}
//
// if we hit the threshold, grab the spinlock and verify we've
// actually done so. this allows us to ignore the spinlock 99%
// of the time.
//
KeAcquireSpinLock ( & fdoData - > SpinLock , & oldIrql ) ;
//
// re-read the value, so we don't run this multiple times
// for a single threshhold being hit. this keeps errorcount
// somewhat useful.
//
succeeded = fdoData - > Perf . SuccessfulIO ;
if ( ( FdoExtension - > ErrorCount ! = 0 ) & &
( fdoData - > Perf . ReEnableThreshhold < = succeeded )
) {
fdoData - > Perf . SuccessfulIO = 0 ; // implicit interlock
NT_ASSERT ( FdoExtension - > ErrorCount > 0 ) ;
errors = InterlockedDecrement ( ( volatile LONG * ) & FdoExtension - > ErrorCount ) ;
//
// note: do in reverse order of the sets "just in case"
//
if ( errors < CLASS_ERROR_LEVEL_2 ) {
if ( errors = = CLASS_ERROR_LEVEL_2 - 1 ) {
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_GENERAL , " ClasspPerfIncrementSuccessfulIo: "
" Error level 2 no longer required. \n " ) ) ;
}
if ( ! TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_DISABLE_DISCONNECT ) ) {
CLEAR_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_DISABLE_DISCONNECT ) ;
}
}
if ( errors < CLASS_ERROR_LEVEL_1 ) {
if ( errors = = CLASS_ERROR_LEVEL_1 - 1 ) {
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_GENERAL , " ClasspPerfIncrementSuccessfulIo: "
" Error level 1 no longer required. \n " ) ) ;
}
if ( ! TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_DISABLE_SYNCH_TRANSFER ) ) {
CLEAR_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_DISABLE_SYNCH_TRANSFER ) ;
}
if ( TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_QUEUE_ACTION_ENABLE ) ) {
SET_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_QUEUE_ACTION_ENABLE ) ;
}
if ( TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_NO_QUEUE_FREEZE ) ) {
SET_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_NO_QUEUE_FREEZE ) ;
}
}
} // end of threshhold definitely being hit for first time
KeReleaseSpinLock ( & fdoData - > SpinLock , oldIrql ) ;
return ;
}
PMDL ClasspBuildDeviceMdl ( PVOID Buffer , ULONG BufferLen , BOOLEAN WriteToDevice )
{
PMDL mdl ;
mdl = IoAllocateMdl ( Buffer , BufferLen , FALSE , FALSE , NULL ) ;
if ( mdl ) {
_SEH2_TRY {
MmProbeAndLockPages ( mdl , KernelMode , WriteToDevice ? IoReadAccess : IoWriteAccess ) ;
# ifdef _MSC_VER
# pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
# endif
} _SEH2_EXCEPT ( EXCEPTION_EXECUTE_HANDLER ) {
NTSTATUS status = _SEH2_GetExceptionCode ( ) ;
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_INIT , " ClasspBuildDeviceMdl: MmProbeAndLockPages failed with %xh. " , status ) ) ;
IoFreeMdl ( mdl ) ;
mdl = NULL ;
} _SEH2_END ;
}
else {
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_INIT , " ClasspBuildDeviceMdl: IoAllocateMdl failed " ) ) ;
}
return mdl ;
}
PMDL BuildDeviceInputMdl ( PVOID Buffer , ULONG BufferLen )
{
return ClasspBuildDeviceMdl ( Buffer , BufferLen , FALSE ) ;
}
VOID ClasspFreeDeviceMdl ( PMDL Mdl )
{
MmUnlockPages ( Mdl ) ;
IoFreeMdl ( Mdl ) ;
}
VOID FreeDeviceInputMdl ( PMDL Mdl )
{
ClasspFreeDeviceMdl ( Mdl ) ;
return ;
}
#if 0
VOID
ClasspPerfResetCounters (
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension - > PrivateFdoData ;
KIRQL oldIrql ;
KeAcquireSpinLock ( & fdoData - > SpinLock , & oldIrql ) ;
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_GENERAL , " ClasspPerfResetCounters: "
" Resetting all perf counters. \n " ) ) ;
fdoData - > Perf . SuccessfulIO = 0 ;
FdoExtension - > ErrorCount = 0 ;
if ( ! TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_DISABLE_DISCONNECT ) ) {
CLEAR_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_DISABLE_DISCONNECT ) ;
}
if ( ! TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_DISABLE_SYNCH_TRANSFER ) ) {
CLEAR_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_DISABLE_SYNCH_TRANSFER ) ;
}
if ( TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_QUEUE_ACTION_ENABLE ) ) {
SET_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_QUEUE_ACTION_ENABLE ) ;
}
if ( TEST_FLAG ( fdoData - > Perf . OriginalSrbFlags ,
SRB_FLAGS_NO_QUEUE_FREEZE ) ) {
SET_FLAG ( FdoExtension - > SrbFlags ,
SRB_FLAGS_NO_QUEUE_FREEZE ) ;
}
KeReleaseSpinLock ( & fdoData - > SpinLock , oldIrql ) ;
return ;
}
# endif
/*++
ClasspDuidGetDeviceIdProperty
Routine Description :
Add StorageDeviceIdProperty to the device unique ID structure .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Return Value :
Status Code
- - */
NTSTATUS
ClasspDuidGetDeviceIdProperty (
IN PDEVICE_OBJECT DeviceObject ,
IN PIRP Irp
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_DESCRIPTOR_HEADER descHeader ;
PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid ;
PUCHAR dest ;
STORAGE_PROPERTY_ID propertyId = StorageDeviceIdProperty ;
NTSTATUS status ;
ULONG queryLength ;
ULONG offset ;
//
// Get the VPD page 83h data.
//
status = ClassGetDescriptor ( commonExtension - > LowerDeviceObject ,
& propertyId ,
( PVOID * ) & deviceIdDescriptor ) ;
if ( ! NT_SUCCESS ( status ) | | ! deviceIdDescriptor ) {
goto FnExit ;
}
queryLength = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
descHeader = Irp - > AssociatedIrp . SystemBuffer ;
//
// Adjust required size and potential destination location.
//
offset = descHeader - > Size ;
dest = ( PUCHAR ) descHeader + offset ;
descHeader - > Size + = deviceIdDescriptor - > Size ;
if ( queryLength < descHeader - > Size ) {
//
// Output buffer is too small. Return error and make sure
// the caller gets info about required buffer size.
//
Irp - > IoStatus . Information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
status = STATUS_BUFFER_OVERFLOW ;
goto FnExit ;
}
storageDuid = Irp - > AssociatedIrp . SystemBuffer ;
storageDuid - > StorageDeviceIdOffset = offset ;
RtlCopyMemory ( dest ,
deviceIdDescriptor ,
deviceIdDescriptor - > Size ) ;
Irp - > IoStatus . Information = storageDuid - > Size ;
status = STATUS_SUCCESS ;
FnExit :
FREE_POOL ( deviceIdDescriptor ) ;
return status ;
}
/*++
ClasspDuidGetDeviceProperty
Routine Description :
Add StorageDeviceProperty to the device unique ID structure .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Return Value :
Status Code
- - */
NTSTATUS
ClasspDuidGetDeviceProperty (
PDEVICE_OBJECT DeviceObject ,
PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = fdoExtension - > DeviceDescriptor ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_DESCRIPTOR_HEADER descHeader ;
PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid ;
PUCHAR dest ;
NTSTATUS status = STATUS_NOT_FOUND ;
ULONG queryLength ;
ULONG offset ;
//
// Use the StorageDeviceProperty already cached in the device extension.
//
if ( ! deviceDescriptor ) {
goto FnExit ;
}
queryLength = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
descHeader = Irp - > AssociatedIrp . SystemBuffer ;
//
// Use this info only if serial number is available.
//
if ( deviceDescriptor - > SerialNumberOffset = = 0 ) {
goto FnExit ;
}
//
// Adjust required size and potential destination location.
//
offset = descHeader - > Size ;
dest = ( PUCHAR ) descHeader + offset ;
descHeader - > Size + = deviceDescriptor - > Size ;
if ( queryLength < descHeader - > Size ) {
//
// Output buffer is too small. Return error and make sure
// the caller get info about required buffer size.
//
Irp - > IoStatus . Information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
status = STATUS_BUFFER_OVERFLOW ;
goto FnExit ;
}
storageDuid = Irp - > AssociatedIrp . SystemBuffer ;
storageDuid - > StorageDeviceOffset = offset ;
RtlCopyMemory ( dest ,
deviceDescriptor ,
deviceDescriptor - > Size ) ;
Irp - > IoStatus . Information = storageDuid - > Size ;
status = STATUS_SUCCESS ;
FnExit :
return status ;
}
/*++
ClasspDuidGetDriveLayout
Routine Description :
Add drive layout signature to the device unique ID structure .
Layout signature is only added for disk - type devices .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Return Value :
Status Code
- - */
NTSTATUS
ClasspDuidGetDriveLayout (
PDEVICE_OBJECT DeviceObject ,
PIRP Irp
)
{
PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_DESCRIPTOR_HEADER descHeader ;
PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid ;
PSTORAGE_DEVICE_LAYOUT_SIGNATURE driveLayoutSignature ;
NTSTATUS status = STATUS_NOT_FOUND ;
ULONG queryLength ;
ULONG offset ;
//
// Only process disk-type devices.
//
if ( DeviceObject - > DeviceType ! = FILE_DEVICE_DISK ) {
goto FnExit ;
}
//
// Get current partition table and process only if GPT
// or MBR layout.
//
status = IoReadPartitionTableEx ( DeviceObject , & layoutEx ) ;
if ( ! NT_SUCCESS ( status ) ) {
status = STATUS_NOT_FOUND ;
goto FnExit ;
}
if ( layoutEx - > PartitionStyle ! = PARTITION_STYLE_GPT & &
layoutEx - > PartitionStyle ! = PARTITION_STYLE_MBR ) {
status = STATUS_NOT_FOUND ;
goto FnExit ;
}
queryLength = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
descHeader = Irp - > AssociatedIrp . SystemBuffer ;
//
// Adjust required size and potential destination location.
//
offset = descHeader - > Size ;
driveLayoutSignature = ( PSTORAGE_DEVICE_LAYOUT_SIGNATURE ) ( ( PUCHAR ) descHeader + offset ) ;
descHeader - > Size + = sizeof ( STORAGE_DEVICE_LAYOUT_SIGNATURE ) ;
if ( queryLength < descHeader - > Size ) {
//
// Output buffer is too small. Return error and make sure
// the caller get info about required buffer size.
//
Irp - > IoStatus . Information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
status = STATUS_BUFFER_OVERFLOW ;
goto FnExit ;
}
storageDuid = Irp - > AssociatedIrp . SystemBuffer ;
driveLayoutSignature - > Size = sizeof ( STORAGE_DEVICE_LAYOUT_SIGNATURE ) ;
driveLayoutSignature - > Version = DUID_VERSION_1 ;
if ( layoutEx - > PartitionStyle = = PARTITION_STYLE_MBR ) {
driveLayoutSignature - > Mbr = TRUE ;
RtlCopyMemory ( & driveLayoutSignature - > DeviceSpecific . MbrSignature ,
& layoutEx - > Mbr . Signature ,
sizeof ( layoutEx - > Mbr . Signature ) ) ;
} else {
driveLayoutSignature - > Mbr = FALSE ;
RtlCopyMemory ( & driveLayoutSignature - > DeviceSpecific . GptDiskId ,
& layoutEx - > Gpt . DiskId ,
sizeof ( layoutEx - > Gpt . DiskId ) ) ;
}
storageDuid - > DriveLayoutSignatureOffset = offset ;
Irp - > IoStatus . Information = storageDuid - > Size ;
status = STATUS_SUCCESS ;
FnExit :
FREE_POOL ( layoutEx ) ;
return status ;
}
/*++
ClasspDuidQueryProperty
Routine Description :
Handles IOCTL_STORAGE_QUERY_PROPERTY for device unique ID requests
( when PropertyId is StorageDeviceUniqueIdProperty ) .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Return Value :
Status Code
- - */
NTSTATUS
ClasspDuidQueryProperty (
PDEVICE_OBJECT DeviceObject ,
PIRP Irp
)
{
PSTORAGE_PROPERTY_QUERY query = Irp - > AssociatedIrp . SystemBuffer ;
PSTORAGE_DESCRIPTOR_HEADER descHeader ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
NTSTATUS status ;
ULONG outLength = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
BOOLEAN includeOptionalIds ;
BOOLEAN overflow = FALSE ;
BOOLEAN infoFound = FALSE ;
BOOLEAN useStatus = TRUE ; // Use the status directly instead of relying on overflow and infoFound flags.
//
// Must run at less then dispatch.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto FnExit ;
}
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
Irp - > IoStatus . Information = 0 ;
status = STATUS_SUCCESS ;
goto FnExit ;
}
if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto FnExit ;
}
//
// Check AdditionalParameters validity.
//
if ( query - > AdditionalParameters [ 0 ] = = DUID_INCLUDE_SOFTWARE_IDS ) {
includeOptionalIds = TRUE ;
} else if ( query - > AdditionalParameters [ 0 ] = = DUID_HARDWARE_IDS_ONLY ) {
includeOptionalIds = FALSE ;
} else {
status = STATUS_INVALID_PARAMETER ;
goto FnExit ;
}
//
// Verify output parameters.
//
if ( outLength < sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
goto FnExit ;
}
//
// From this point forward the status depends on the overflow
// and infoFound flags.
//
useStatus = FALSE ;
descHeader = Irp - > AssociatedIrp . SystemBuffer ;
RtlZeroMemory ( descHeader , outLength ) ;
descHeader - > Version = DUID_VERSION_1 ;
descHeader - > Size = sizeof ( STORAGE_DEVICE_UNIQUE_IDENTIFIER ) ;
//
// Try to build device unique id from StorageDeviceIdProperty.
//
status = ClasspDuidGetDeviceIdProperty ( DeviceObject ,
Irp ) ;
if ( status = = STATUS_BUFFER_OVERFLOW ) {
overflow = TRUE ;
}
if ( NT_SUCCESS ( status ) ) {
infoFound = TRUE ;
}
//
// Try to build device unique id from StorageDeviceProperty.
//
status = ClasspDuidGetDeviceProperty ( DeviceObject ,
Irp ) ;
if ( status = = STATUS_BUFFER_OVERFLOW ) {
overflow = TRUE ;
}
if ( NT_SUCCESS ( status ) ) {
infoFound = TRUE ;
}
//
// The following portion is optional and only included if the
// caller requested software IDs.
//
if ( ! includeOptionalIds ) {
goto FnExit ;
}
//
// Try to build device unique id from drive layout signature (disk
// devices only).
//
status = ClasspDuidGetDriveLayout ( DeviceObject ,
Irp ) ;
if ( status = = STATUS_BUFFER_OVERFLOW ) {
overflow = TRUE ;
}
if ( NT_SUCCESS ( status ) ) {
infoFound = TRUE ;
}
FnExit :
if ( ! useStatus ) {
//
// Return overflow, success, or a generic error.
//
if ( overflow ) {
//
// If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
// success to the user. Otherwise, send an error so the user
// knows a larger buffer is required.
//
if ( outLength = = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
status = STATUS_SUCCESS ;
Irp - > IoStatus . Information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
} else {
status = STATUS_BUFFER_OVERFLOW ;
}
} else if ( infoFound ) {
status = STATUS_SUCCESS ;
//
// Exercise the compare routine. This should always succeed.
//
NT_ASSERT ( DuidExactMatch = = CompareStorageDuids ( Irp - > AssociatedIrp . SystemBuffer ,
Irp - > AssociatedIrp . SystemBuffer ) ) ;
} else {
status = STATUS_NOT_FOUND ;
}
}
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
/*++////////////////////////////////////////////////////////////////////////////
ClasspWriteCacheProperty ( )
Routine Description :
This routine reads the caching mode page from the device to
build the Write Cache property page .
Arguments :
DeviceObject - The device object to handle this irp
Irp - The IRP for this request
Srb - SRB allocated by the dispatch routine
Return Value :
- - */
NTSTATUS ClasspWriteCacheProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PSTORAGE_WRITE_CACHE_PROPERTY writeCache ;
PSTORAGE_PROPERTY_QUERY query = Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PMODE_PARAMETER_HEADER modeData = NULL ;
PMODE_CACHING_PAGE pageData = NULL ;
ULONG length , information = 0 ;
NTSTATUS status ;
PCDB cdb ;
//
// Must run at less then dispatch.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto WriteCacheExit ;
}
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
status = STATUS_SUCCESS ;
goto WriteCacheExit ;
}
if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto WriteCacheExit ;
}
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length < sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
goto WriteCacheExit ;
}
writeCache = ( PSTORAGE_WRITE_CACHE_PROPERTY ) Irp - > AssociatedIrp . SystemBuffer ;
RtlZeroMemory ( writeCache , length ) ;
//
// Set version and required size.
//
writeCache - > Version = sizeof ( STORAGE_WRITE_CACHE_PROPERTY ) ;
writeCache - > Size = sizeof ( STORAGE_WRITE_CACHE_PROPERTY ) ;
if ( length < sizeof ( STORAGE_WRITE_CACHE_PROPERTY ) ) {
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
status = STATUS_SUCCESS ;
goto WriteCacheExit ;
}
//
// Set known values
//
writeCache - > NVCacheEnabled = FALSE ;
writeCache - > UserDefinedPowerProtection = TEST_FLAG ( fdoExtension - > DeviceFlags , DEV_POWER_PROTECTED ) ;
//
// Check for flush cache support by sending a sync cache command
// to the device.
//
//
// Set timeout value and mark the request as not being a tagged request.
//
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue * 4 ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , fdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , 10 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > CDB10 . OperationCode = SCSIOP_SYNCHRONIZE_CACHE ;
status = ClassSendSrbSynchronous ( DeviceObject ,
Srb ,
NULL ,
0 ,
TRUE ) ;
if ( NT_SUCCESS ( status ) ) {
writeCache - > FlushCacheSupported = TRUE ;
} else {
//
// Device does not support sync cache
//
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Synchronize cache failed with status 0x%X \n " , status ) ) ;
writeCache - > FlushCacheSupported = FALSE ;
//
// Reset the status if there was any failure
//
status = STATUS_SUCCESS ;
}
modeData = ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned ,
MODE_PAGE_DATA_SIZE ,
CLASS_TAG_MODE_DATA ) ;
if ( modeData = = NULL ) {
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Unable to allocate mode data buffer \n " ) ) ;
status = STATUS_INSUFFICIENT_RESOURCES ;
goto WriteCacheExit ;
}
RtlZeroMemory ( modeData , MODE_PAGE_DATA_SIZE ) ;
length = ClassModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CACHING ) ;
if ( length < sizeof ( MODE_PARAMETER_HEADER ) ) {
//
// Retry the request in case of a check condition.
//
length = ClassModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CACHING ) ;
if ( length < sizeof ( MODE_PARAMETER_HEADER ) ) {
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Mode Sense failed \n " ) ) ;
status = STATUS_IO_DEVICE_ERROR ;
goto WriteCacheExit ;
}
}
//
// If the length is greater than length indicated by the mode data reset
// the data to the mode data.
//
if ( length > ( ULONG ) ( modeData - > ModeDataLength + 1 ) ) {
length = modeData - > ModeDataLength + 1 ;
}
//
// Look for caching page in the returned mode page data.
//
pageData = ClassFindModePage ( ( PCHAR ) modeData ,
length ,
MODE_PAGE_CACHING ,
TRUE ) ;
//
// Check if valid caching page exists.
//
if ( pageData = = NULL ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Unable to find caching mode page. \n " ) ) ;
//
// Set write cache value as unknown.
//
writeCache - > WriteCacheEnabled = WriteCacheEnableUnknown ;
writeCache - > WriteCacheType = WriteCacheTypeUnknown ;
} else {
writeCache - > WriteCacheEnabled = pageData - > WriteCacheEnable ?
WriteCacheEnabled : WriteCacheDisabled ;
writeCache - > WriteCacheType = pageData - > WriteCacheEnable ?
WriteCacheTypeWriteBack : WriteCacheTypeUnknown ;
}
//
// Check write through support. If the device previously failed a write request
// with FUA bit is set, then CLASS_SPECIAL_FUA_NOT_SUPPORTED will be set,
// which means write through is not support by the device.
//
if ( ( modeData - > DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED ) & &
( ! TEST_FLAG ( fdoExtension - > ScanForSpecialFlags , CLASS_SPECIAL_FUA_NOT_SUPPORTED ) ) ) {
writeCache - > WriteThroughSupported = WriteThroughSupported ;
} else {
writeCache - > WriteThroughSupported = WriteThroughNotSupported ;
}
//
// Get the changeable caching mode page and check write cache is changeable.
//
RtlZeroMemory ( modeData , MODE_PAGE_DATA_SIZE ) ;
length = ClasspModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CACHING ,
MODE_SENSE_CHANGEABLE_VALUES ) ;
if ( length < sizeof ( MODE_PARAMETER_HEADER ) ) {
//
// Retry the request in case of a check condition.
//
length = ClasspModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CACHING ,
MODE_SENSE_CHANGEABLE_VALUES ) ;
if ( length < sizeof ( MODE_PARAMETER_HEADER ) ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Mode Sense failed \n " ) ) ;
//
// If the device fails to return changeable pages, then
// set the write cache changeable value to unknown.
//
writeCache - > WriteCacheChangeable = WriteCacheChangeUnknown ;
information = sizeof ( STORAGE_WRITE_CACHE_PROPERTY ) ;
goto WriteCacheExit ;
}
}
//
// If the length is greater than length indicated by the mode data reset
// the data to the mode data.
//
if ( length > ( ULONG ) ( modeData - > ModeDataLength + 1 ) ) {
length = modeData - > ModeDataLength + 1 ;
}
//
// Look for caching page in the returned mode page data.
//
pageData = ClassFindModePage ( ( PCHAR ) modeData ,
length ,
MODE_PAGE_CACHING ,
TRUE ) ;
//
// Check if valid caching page exists.
//
if ( pageData = = NULL ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION , TRACE_FLAG_IOCTL , " ClasspWriteCacheProperty: Unable to find caching mode page. \n " ) ) ;
//
// Set write cache changeable value to unknown.
//
writeCache - > WriteCacheChangeable = WriteCacheChangeUnknown ;
} else {
writeCache - > WriteCacheChangeable = pageData - > WriteCacheEnable ?
WriteCacheChangeable : WriteCacheNotChangeable ;
}
information = sizeof ( STORAGE_WRITE_CACHE_PROPERTY ) ;
WriteCacheExit :
FREE_POOL ( modeData ) ;
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ; ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
ULONG
ClasspCalculateLogicalSectorSize (
_In_ PDEVICE_OBJECT Fdo ,
_In_ ULONG BytesPerBlockInBigEndian
)
/*++
Convert the big - endian value .
if it ' s 0 , default to the standard 512 bytes .
if it ' s not a power of 2 value , round down to power of 2.
- - */
{
ULONG logicalSectorSize ;
REVERSE_BYTES ( & logicalSectorSize , & BytesPerBlockInBigEndian ) ;
if ( logicalSectorSize = = 0 ) {
logicalSectorSize = 512 ;
} else {
//
// Clear all but the highest set bit.
// That will give us a bytesPerSector value that is a power of 2.
//
if ( logicalSectorSize & ( logicalSectorSize - 1 ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_INIT , " FDO %ph has non-standard sector size 0x%x. " , Fdo , logicalSectorSize ) ) ;
do {
logicalSectorSize & = logicalSectorSize - 1 ;
}
while ( logicalSectorSize & ( logicalSectorSize - 1 ) ) ;
}
}
return logicalSectorSize ;
}
NTSTATUS
InterpretReadCapacity16Data (
_Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
)
{
NTSTATUS status = STATUS_SUCCESS ;
USHORT lowestAlignedBlock ;
USHORT logicalBlocksPerPhysicalBlock ;
PCLASS_READ_CAPACITY16_DATA cachedData = & ( FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data ) ;
// use Logical Sector Size from DiskGeometry to avoid duplicated calculation.
FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerLogicalSector = ClasspCalculateLogicalSectorSize ( FdoExtension - > DeviceObject , ReadCapacity16Data - > BytesPerBlock ) ;
// FdoExtension->DiskGeometry.BytesPerSector might be 0 for class drivers that don't get READ CAPACITY info yet.
NT_ASSERT ( ( FdoExtension - > DiskGeometry . BytesPerSector = = 0 ) | |
( FdoExtension - > DiskGeometry . BytesPerSector = = FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerLogicalSector ) ) ;
logicalBlocksPerPhysicalBlock = 1 < < ReadCapacity16Data - > LogicalPerPhysicalExponent ;
lowestAlignedBlock = ( ReadCapacity16Data - > LowestAlignedBlock_MSB < < 8 ) | ReadCapacity16Data - > LowestAlignedBlock_LSB ;
if ( lowestAlignedBlock > logicalBlocksPerPhysicalBlock ) {
// we get garbage data
status = STATUS_UNSUCCESSFUL ;
} else {
// value of lowestAlignedBlock (from T10 spec) needs to be converted.
lowestAlignedBlock = ( logicalBlocksPerPhysicalBlock - lowestAlignedBlock ) % logicalBlocksPerPhysicalBlock ;
}
if ( NT_SUCCESS ( status ) ) {
// fill output buffer
cachedData - > BytesPerPhysicalSector = cachedData - > BytesPerLogicalSector * logicalBlocksPerPhysicalBlock ;
cachedData - > BytesOffsetForSectorAlignment = cachedData - > BytesPerLogicalSector * lowestAlignedBlock ;
//
// Fill in the Logical Block Provisioning info. Note that we do not
// use these fields; we use the Provisioning Type and LBPRZ fields from
// the Logical Block Provisioning VPD page (0xB2).
//
cachedData - > LBProvisioningEnabled = ReadCapacity16Data - > LBPME ;
cachedData - > LBProvisioningReadZeros = ReadCapacity16Data - > LBPRZ ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_INIT ,
" InterpretReadCapacity16Data: Device \' s LBP enabled = %d \n " ,
cachedData - > LBProvisioningEnabled ) ) ;
}
return status ;
}
NTSTATUS
ClassReadCapacity16 (
_Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
This routine may send down a READ CAPACITY 16 command to retrieve info .
The info will be cached in FdoExtension - > LowerLayerSupport - > AccessAlignment .
After info retrieving finished , this function sets following field :
FdoExtension - > LowerLayerSupport - > AccessAlignment . LowerLayerSupported = Supported ;
to indicate that info has been cached .
NOTE : some future processes may use this function to send the command anyway , it will be caller ' s decision
on checking ' AccessAlignment . LowerLayerSupported ' in case the cached info is good enough .
*/
{
NTSTATUS status = STATUS_SUCCESS ;
PREAD_CAPACITY16_DATA dataBuffer = NULL ;
UCHAR bufferLength = sizeof ( READ_CAPACITY16_DATA ) ;
ULONG allocationBufferLength = bufferLength ; //DMA buffer size for alignment
PCDB cdb ;
ULONG dataTransferLength = 0 ;
//
// If the information retrieval has already been attempted, return the cached status.
//
if ( FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . CommandStatus ! = - 1 ) {
// get cached NTSTATUS from previous call.
return FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . CommandStatus ;
}
if ( ClasspIsObsoletePortDriver ( FdoExtension ) ) {
FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . CommandStatus = STATUS_NOT_IMPLEMENTED ;
return STATUS_NOT_IMPLEMENTED ;
}
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
allocationBufferLength = ALIGN_UP_BY ( allocationBufferLength , KeGetRecommendedSharedDataAlignment ( ) ) ;
dataBuffer = ( PREAD_CAPACITY16_DATA ) ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , allocationBufferLength , ' 4 CcS ' ) ;
# else
dataBuffer = ( PREAD_CAPACITY16_DATA ) ExAllocatePoolWithTag ( NonPagedPoolNx , bufferLength , ' 4 CcS ' ) ;
# endif
if ( dataBuffer = = NULL ) {
// return without updating FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus
// the field will remain value as "-1", so that the command will be attempted next time this function is called.
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlZeroMemory ( dataBuffer , allocationBufferLength ) ;
//
// Initialize the SRB.
//
if ( FdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) {
status = InitializeStorageRequestBlock ( ( PSTORAGE_REQUEST_BLOCK ) Srb ,
STORAGE_ADDRESS_TYPE_BTL8 ,
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE ,
1 ,
SrbExDataTypeScsiCdb16 ) ;
if ( NT_SUCCESS ( status ) ) {
( ( PSTORAGE_REQUEST_BLOCK ) Srb ) - > SrbFunction = SRB_FUNCTION_EXECUTE_SCSI ;
} else {
//
// Should not occur.
//
NT_ASSERT ( FALSE ) ;
}
} else {
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = SRB_FUNCTION_EXECUTE_SCSI ;
}
//prepare the Srb
if ( NT_SUCCESS ( status ) )
{
SrbSetTimeOutValue ( Srb , FdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , FdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , 16 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > READ_CAPACITY16 . OperationCode = SCSIOP_READ_CAPACITY16 ;
cdb - > READ_CAPACITY16 . ServiceAction = SERVICE_ACTION_READ_CAPACITY16 ;
cdb - > READ_CAPACITY16 . AllocationLength [ 3 ] = bufferLength ;
status = ClassSendSrbSynchronous ( FdoExtension - > DeviceObject ,
Srb ,
dataBuffer ,
allocationBufferLength ,
FALSE ) ;
dataTransferLength = SrbGetDataTransferLength ( Srb ) ;
}
if ( NT_SUCCESS ( status ) & & ( dataTransferLength < 16 ) )
{
// the device should return at least 16 bytes of data for this command.
status = STATUS_INFO_LENGTH_MISMATCH ;
}
//
// Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
// buffer was larger than necessary.
//
if ( status = = STATUS_DATA_OVERRUN & & dataTransferLength < bufferLength )
{
status = STATUS_SUCCESS ;
}
if ( NT_SUCCESS ( status ) )
{
// cache data into FdoExtension
status = InterpretReadCapacity16Data ( FdoExtension , dataBuffer ) ;
}
// cache the status indicates that this funciton has been called.
FdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . CommandStatus = status ;
ExFreePool ( dataBuffer ) ;
return status ;
}
NTSTATUS ClasspAccessAlignmentProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
At first time of receiving the request , this function will forward it to lower stack to determine if it ' s supportted .
If it ' s not supported , SCSIOP_READ_CAPACITY16 will be sent down to retrieve the information .
*/
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSTORAGE_PROPERTY_QUERY query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
ULONG length = 0 ;
ULONG information = 0 ;
PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR accessAlignment ;
//
// check registry setting and fail the IOCTL if it's required.
// this registry setting can be used to work around issues which upper layer doesn't support large physical sector size.
//
if ( fdoExtension - > FunctionSupportInfo - > RegAccessAlignmentQueryNotSupported ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit ;
}
if ( ( DeviceObject - > DeviceType ! = FILE_DEVICE_DISK ) | |
( TEST_FLAG ( DeviceObject - > Characteristics , FILE_FLOPPY_DISKETTE ) ) | |
( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . AccessAlignmentProperty = = Supported ) ) {
// if it's not disk, forward the request to lower layer,
// if the IOCTL is supported by lower stack, forward it down.
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
return status ;
}
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
status = STATUS_SUCCESS ;
goto Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated before entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
// do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
accessAlignment = ( PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length < sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ) {
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
accessAlignment - > Version = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
accessAlignment - > Size = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
goto Exit ;
}
status = STATUS_BUFFER_TOO_SMALL ;
goto Exit ;
}
// not support Cache Line,
// 'BytesPerCacheLine' and 'BytesOffsetForCacheAlignment' fields are zero-ed.
//
// note that 'Supported' case has been handled at the beginning of this function.
//
switch ( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . AccessAlignmentProperty ) {
case SupportUnknown : {
// send down request and wait for the request to complete.
status = ClassForwardIrpSynchronous ( commonExtension , Irp ) ;
if ( ClasspLowerLayerNotSupport ( status ) ) {
// case 1: the request is not supported by lower layer, sends down command
// some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
// ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
if ( ClasspIsObsoletePortDriver ( fdoExtension ) = = FALSE ) {
status = ClassReadCapacity16 ( fdoExtension , Srb ) ;
} else {
fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . CommandStatus = status ;
}
// data is ready in fdoExtension
// set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . AccessAlignmentProperty = NotSupported ;
if ( NT_SUCCESS ( status ) ) {
// fill output buffer
RtlZeroMemory ( accessAlignment , length ) ;
accessAlignment - > Version = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
accessAlignment - > Size = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
accessAlignment - > BytesPerLogicalSector = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerLogicalSector ;
accessAlignment - > BytesPerPhysicalSector = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerPhysicalSector ;
accessAlignment - > BytesOffsetForSectorAlignment = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesOffsetForSectorAlignment ;
// set returned data length
information = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
} else {
information = 0 ;
}
} else {
// case 2: the request is supported and it completes successfully
// case 3: the request is supported by lower stack but other failure status is returned.
// from now on, the same request will be send down to lower stack directly.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . AccessAlignmentProperty = Supported ;
information = ( ULONG ) Irp - > IoStatus . Information ;
}
goto Exit ;
break ;
}
case NotSupported : {
// ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
status = ClassReadCapacity16 ( fdoExtension , Srb ) ;
if ( NT_SUCCESS ( status ) ) {
RtlZeroMemory ( accessAlignment , length ) ;
accessAlignment - > Version = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
accessAlignment - > Size = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
accessAlignment - > BytesPerLogicalSector = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerLogicalSector ;
accessAlignment - > BytesPerPhysicalSector = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesPerPhysicalSector ;
accessAlignment - > BytesOffsetForSectorAlignment = fdoExtension - > FunctionSupportInfo - > ReadCapacity16Data . BytesOffsetForSectorAlignment ;
information = sizeof ( STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR ) ;
} else {
information = 0 ;
}
goto Exit ;
break ;
}
case Supported : {
NT_ASSERT ( FALSE ) ; // this case is handled at the beginning of the function.
status = STATUS_INTERNAL_ERROR ;
break ;
}
} // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty)
Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
2020-12-05 17:52:13 +00:00
static
2020-08-29 03:06:22 +00:00
NTSTATUS
IncursSeekPenalty (
_In_ USHORT MediumRotationRate ,
_In_ PBOOLEAN IncursSeekPenalty
)
{
NTSTATUS status ;
if ( MediumRotationRate = = 0x0001 ) {
// Non-rotating media (e.g., solid state device)
* IncursSeekPenalty = FALSE ;
status = STATUS_SUCCESS ;
} else if ( ( MediumRotationRate > = 0x401 ) & &
( MediumRotationRate < = 0xFFFE ) ) {
// Nominal media rotation rate in rotations per minute (rpm)
* IncursSeekPenalty = TRUE ;
status = STATUS_SUCCESS ;
} else {
// Unknown cases:
// 0 - Rate not reported
// 0002h-0400h - Reserved
// FFFFh - Reserved
status = STATUS_UNSUCCESSFUL ;
}
return status ;
}
NTSTATUS
ClasspDeviceMediaTypeProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description :
This routine returns the medium product type reported by the device for the associated LU .
This function must be called at IRQL < DISPATCH_LEVEL .
Arguments :
DeviceObject - Supplies the device object associated with this request
Irp - The IRP to be processed
Srb - The SRB associated with the request
Return Value :
NTSTATUS code
- - */
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSTORAGE_PROPERTY_QUERY query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR pDesc = ( PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack ;
ULONG length = 0 ;
ULONG information = 0 ;
irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceMediaTypeProperty (%p): Entering function. \n " ,
DeviceObject ) ) ;
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
//
// In order to maintain consistency with the how the rest of the properties
// are handled, always return success for PropertyExistsQuery.
//
status = STATUS_SUCCESS ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceMediaTypeProperty (%p): Unsupported query type %x for media type property. \n " ,
DeviceObject ,
query - > QueryType ) ) ;
status = STATUS_NOT_SUPPORTED ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
//
// Validate the request.
// InputBufferLength and IsFdo have already been validated.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceMediaTypeProperty (%p): Query property for media type at incorrect IRQL. \n " ,
DeviceObject ) ) ;
status = STATUS_INVALID_LEVEL ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
information = sizeof ( STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR ) ;
pDesc - > Version = sizeof ( STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR ) ;
pDesc - > Size = sizeof ( STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR ) ;
} else {
status = STATUS_BUFFER_TOO_SMALL ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
if ( length < sizeof ( STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR ) ) {
status = STATUS_SUCCESS ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
//
// Only query BlockDeviceCharacteristics VPD page if device support has been confirmed.
//
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceCharacteristics = = TRUE ) {
status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage ( fdoExtension , Srb ) ;
} else {
//
// Otherwise device was previously found lacking support for this VPD page. Fail the request.
//
status = STATUS_INVALID_DEVICE_REQUEST ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
if ( ! NT_SUCCESS ( status ) ) {
status = fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . CommandStatus ;
information = 0 ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetBlockDeviceCharacteristicsVPDPage (%p): VPD retrieval fails with %x. \n " ,
DeviceObject ,
status ) ) ;
goto __ClasspDeviceMediaTypeProperty_Exit ;
}
//
// Fill in the output buffer. All data is copied from the FDO extension, cached
// from device response to earlier VPD_BLOCK_DEVICE_CHARACTERISTICS query.
//
pDesc - > MediumProductType = fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . MediumProductType ;
status = STATUS_SUCCESS ;
__ClasspDeviceMediaTypeProperty_Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceMediaTypeProperty (%p): Exiting function with status %x. \n " ,
DeviceObject ,
status ) ) ;
return status ;
}
NTSTATUS ClasspDeviceGetBlockDeviceCharacteristicsVPDPage (
_In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension ,
_In_ PSCSI_REQUEST_BLOCK Srb
)
/*
Routine Description :
This function sends an INQUIRY command request for VPD_BLOCK_DEVICE_CHARACTERISTICS to
the device . Relevant data from the response is cached in the FDO extension .
Arguments :
FdoExtension : The FDO extension of the device to which the INQUIRY command will be sent .
Srb : Allocated by the caller .
SrbSize : The size of the Srb buffer in bytes .
Return Value :
STATUS_INVALID_PARAMETER : May be returned if the LogPage buffer is NULL or
not large enough .
STATUS_SUCCESS : The log page was obtained and placed in the LogPage buffer .
This function may return other NTSTATUS codes from internal function calls .
- - */
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PCDB cdb ;
UCHAR bufferLength = sizeof ( VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE ) ; // data is 64 bytes
ULONG allocationBufferLength = bufferLength ;
PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE dataBuffer = NULL ;
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
allocationBufferLength = ALIGN_UP_BY ( allocationBufferLength , KeGetRecommendedSharedDataAlignment ( ) ) ;
dataBuffer = ( PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE ) ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned ,
allocationBufferLength ,
' 5 CcS '
) ;
# else
dataBuffer = ( PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE ) ExAllocatePoolWithTag ( NonPagedPoolNx ,
bufferLength ,
' 5 CcS '
) ;
# endif
if ( dataBuffer = = NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit ;
}
RtlZeroMemory ( dataBuffer , allocationBufferLength ) ;
// prepare the Srb
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , fdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , 6 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > CDB6INQUIRY3 . OperationCode = SCSIOP_INQUIRY ;
cdb - > CDB6INQUIRY3 . EnableVitalProductData = 1 ; //EVPD bit
cdb - > CDB6INQUIRY3 . PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS ;
cdb - > CDB6INQUIRY3 . AllocationLength = bufferLength ; //AllocationLength field in CDB6INQUIRY3 is only one byte.
status = ClassSendSrbSynchronous ( fdoExtension - > CommonExtension . DeviceObject ,
Srb ,
dataBuffer ,
allocationBufferLength ,
FALSE ) ;
if ( NT_SUCCESS ( status ) ) {
if ( SrbGetDataTransferLength ( Srb ) < 0x8 ) {
// the device should return at least 8 bytes of data for use.
status = STATUS_UNSUCCESSFUL ;
} else if ( ( dataBuffer - > PageLength ! = 0x3C ) | | ( dataBuffer - > PageCode ! = VPD_BLOCK_DEVICE_CHARACTERISTICS ) ) {
// 'PageLength' shall be 0x3C; and 'PageCode' shall match.
status = STATUS_UNSUCCESSFUL ;
} else {
// cache data into fdoExtension
fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . MediumRotationRate = ( dataBuffer - > MediumRotationRateMsb < < 8 ) |
dataBuffer - > MediumRotationRateLsb ;
fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . MediumProductType = dataBuffer - > MediumProductType ;
fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . NominalFormFactor = dataBuffer - > NominalFormFactor ;
}
} else {
// the command failed, surface up the command error from 'status' variable. Nothing to do here.
}
Exit :
if ( dataBuffer ! = NULL ) {
ExFreePool ( dataBuffer ) ;
}
return status ;
}
NTSTATUS ClasspDeviceSeekPenaltyProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
At first time of receiving the request , this function will forward it to lower stack to determine if it ' s supportted .
If it ' s not supported , INQUIRY ( Block Device Characteristics VPD page ) will be sent down to retrieve the information .
*/
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSTORAGE_PROPERTY_QUERY query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
ULONG length = 0 ;
ULONG information = 0 ;
BOOLEAN incursSeekPenalty = TRUE ;
PDEVICE_SEEK_PENALTY_DESCRIPTOR seekPenalty ;
if ( ( DeviceObject - > DeviceType ! = FILE_DEVICE_DISK ) | |
( TEST_FLAG ( DeviceObject - > Characteristics , FILE_FLOPPY_DISKETTE ) ) | |
( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . SeekPenaltyProperty = = Supported ) ) {
// if it's not disk, forward the request to lower layer,
// if the IOCTL is supported by lower stack, forward it down.
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
return status ;
}
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
status = STATUS_SUCCESS ;
goto Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
// do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
seekPenalty = ( PDEVICE_SEEK_PENALTY_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length < sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ) {
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
seekPenalty - > Version = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
seekPenalty - > Size = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
goto Exit ;
}
status = STATUS_BUFFER_TOO_SMALL ;
goto Exit ;
}
//
// note that 'Supported' case has been handled at the beginning of this function.
//
switch ( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . SeekPenaltyProperty ) {
case SupportUnknown : {
// send down request and wait for the request to complete.
status = ClassForwardIrpSynchronous ( commonExtension , Irp ) ;
if ( ClasspLowerLayerNotSupport ( status ) ) {
// case 1: the request is not supported by lower layer, sends down command
// some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
// send INQUIRY command if the VPD page is supported.
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceCharacteristics = = TRUE ) {
status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage ( fdoExtension , Srb ) ;
} else {
// the INQUIRY - VPD page command to discover the info is not supported, fail the request.
status = STATUS_INVALID_DEVICE_REQUEST ;
}
if ( NT_SUCCESS ( status ) ) {
status = IncursSeekPenalty ( fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . MediumRotationRate , & incursSeekPenalty ) ;
}
fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . CommandStatus = status ;
// data is ready in fdoExtension
// set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . SeekPenaltyProperty = NotSupported ;
// fill output buffer
if ( NT_SUCCESS ( status ) ) {
RtlZeroMemory ( seekPenalty , length ) ;
seekPenalty - > Version = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
seekPenalty - > Size = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
seekPenalty - > IncursSeekPenalty = incursSeekPenalty ;
information = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
} else {
information = 0 ;
}
} else {
// case 2: the request is supported and it completes successfully
// case 3: the request is supported by lower stack but other failure status is returned.
// from now on, the same request will be send down to lower stack directly.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . SeekPenaltyProperty = Supported ;
information = ( ULONG ) Irp - > IoStatus . Information ;
}
goto Exit ;
break ;
}
case NotSupported : {
status = fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . CommandStatus ;
if ( NT_SUCCESS ( status ) ) {
status = IncursSeekPenalty ( fdoExtension - > FunctionSupportInfo - > DeviceCharacteristicsData . MediumRotationRate , & incursSeekPenalty ) ;
}
if ( NT_SUCCESS ( status ) ) {
RtlZeroMemory ( seekPenalty , length ) ;
seekPenalty - > Version = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
seekPenalty - > Size = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
seekPenalty - > IncursSeekPenalty = incursSeekPenalty ;
information = sizeof ( DEVICE_SEEK_PENALTY_DESCRIPTOR ) ;
} else {
information = 0 ;
}
goto Exit ;
break ;
}
case Supported : {
NT_ASSERT ( FALSE ) ; // this case is handled at the begining of the function.
break ;
}
} // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty)
Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ; ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
NTSTATUS ClasspDeviceGetLBProvisioningVPDPage (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_opt_ PSCSI_REQUEST_BLOCK Srb
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
USHORT pageLength = 0 ;
PVOID dataBuffer = NULL ;
UCHAR bufferLength = VPD_MAX_BUFFER_SIZE ; // use biggest buffer possible
ULONG allocationBufferLength = bufferLength ; // Since the CDB size may differ from the actual buffer allocation
PCDB cdb ;
ULONG dataTransferLength = 0 ;
PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE lbProvisioning = NULL ;
//
// if the informaiton has been attempted to retrieve, return the cached status.
//
if ( fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus ! = - 1 ) {
// get cached NTSTATUS from previous call.
return fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus ;
}
//
// Initialize LBProvisioningData fields to 'unsupported' defaults.
//
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ProvisioningType = PROVISIONING_TYPE_UNKNOWN ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPRZ = FALSE ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPU = FALSE ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ANC_SUP = FALSE ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ThresholdExponent = 0 ;
//
// Try to get the Thin Provisioning VPD page (0xB2), if it is supported.
//
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . LBProvisioning = = TRUE & &
Srb ! = NULL )
{
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
//
allocationBufferLength = ALIGN_UP_BY ( allocationBufferLength , KeGetRecommendedSharedDataAlignment ( ) ) ;
dataBuffer = ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , allocationBufferLength , ' 0 CcS ' ) ;
# else
dataBuffer = ExAllocatePoolWithTag ( NonPagedPoolNx , bufferLength , ' 0 CcS ' ) ;
# endif
if ( dataBuffer = = NULL ) {
// return without updating FdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus
// the field will remain value as "-1", so that the command will be attempted next time this function is called.
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit ;
}
lbProvisioning = ( PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE ) dataBuffer ;
RtlZeroMemory ( dataBuffer , allocationBufferLength ) ;
if ( fdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) {
status = InitializeStorageRequestBlock ( ( PSTORAGE_REQUEST_BLOCK ) Srb ,
STORAGE_ADDRESS_TYPE_BTL8 ,
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE ,
1 ,
SrbExDataTypeScsiCdb16 ) ;
if ( NT_SUCCESS ( status ) ) {
( ( PSTORAGE_REQUEST_BLOCK ) Srb ) - > SrbFunction = SRB_FUNCTION_EXECUTE_SCSI ;
} else {
//
// Should not occur.
//
NT_ASSERT ( FALSE ) ;
}
} else {
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = SRB_FUNCTION_EXECUTE_SCSI ;
status = STATUS_SUCCESS ;
}
if ( NT_SUCCESS ( status ) ) {
// prepare the Srb
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , fdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , 6 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > CDB6INQUIRY3 . OperationCode = SCSIOP_INQUIRY ;
cdb - > CDB6INQUIRY3 . EnableVitalProductData = 1 ; //EVPD bit
cdb - > CDB6INQUIRY3 . PageCode = VPD_LOGICAL_BLOCK_PROVISIONING ;
cdb - > CDB6INQUIRY3 . AllocationLength = bufferLength ; //AllocationLength field in CDB6INQUIRY3 is only one byte.
status = ClassSendSrbSynchronous ( fdoExtension - > DeviceObject ,
Srb ,
dataBuffer ,
allocationBufferLength ,
FALSE ) ;
dataTransferLength = SrbGetDataTransferLength ( Srb ) ;
}
//
// Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
// buffer was larger than necessary.
//
if ( status = = STATUS_DATA_OVERRUN & & dataTransferLength < bufferLength )
{
status = STATUS_SUCCESS ;
}
if ( NT_SUCCESS ( status ) ) {
REVERSE_BYTES_SHORT ( & pageLength , & ( lbProvisioning - > PageLength ) ) ;
}
if ( NT_SUCCESS ( status ) & &
( ( dataTransferLength < 0x08 ) | |
( pageLength < ( FIELD_OFFSET ( VPD_LOGICAL_BLOCK_PROVISIONING_PAGE , Reserved2 ) - FIELD_OFFSET ( VPD_LOGICAL_BLOCK_PROVISIONING_PAGE , ThresholdExponent ) ) ) | |
( lbProvisioning - > PageCode ! = VPD_LOGICAL_BLOCK_PROVISIONING ) ) ) {
// the device should return at least 8 bytes of data for use.
// 'PageCode' shall match and we need all the relevant data after the header.
status = STATUS_INFO_LENGTH_MISMATCH ;
}
//
// Fill in the FDO extension with either the data from the VPD page, or
// use defaults if there was an error.
//
if ( NT_SUCCESS ( status ) )
{
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ProvisioningType = lbProvisioning - > ProvisioningType ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPRZ = lbProvisioning - > LBPRZ ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPU = lbProvisioning - > LBPU ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ANC_SUP = lbProvisioning - > ANC_SUP ;
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ThresholdExponent = lbProvisioning - > ThresholdExponent ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_PNP ,
" ClasspDeviceGetLBProvisioningVPDPage (%p): %s %s (rev %s) reported following parameters: \
\ n \ t \ t \ tProvisioningType : % u \
\ n \ t \ t \ tLBPRZ : % u \
\ n \ t \ t \ tLBPU : % u \
\ n \ t \ t \ tANC_SUP : % I64u \
\ n \ t \ t \ tThresholdExponent : % u \ n " ,
DeviceObject ,
( PCSZ ) ( ( ( PUCHAR ) fdoExtension - > DeviceDescriptor ) + fdoExtension - > DeviceDescriptor - > VendorIdOffset ) ,
( PCSZ ) ( ( ( PUCHAR ) fdoExtension - > DeviceDescriptor ) + fdoExtension - > DeviceDescriptor - > ProductIdOffset ) ,
( PCSZ ) ( ( ( PUCHAR ) fdoExtension - > DeviceDescriptor ) + fdoExtension - > DeviceDescriptor - > ProductRevisionOffset ) ,
lbProvisioning - > ProvisioningType ,
lbProvisioning - > LBPRZ ,
lbProvisioning - > LBPU ,
lbProvisioning - > ANC_SUP ,
lbProvisioning - > ThresholdExponent ) ) ;
}
} else {
status = STATUS_INVALID_DEVICE_REQUEST ;
}
fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus = status ;
Exit :
FREE_POOL ( dataBuffer ) ;
return status ;
}
NTSTATUS ClasspDeviceGetBlockLimitsVPDPage (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_Inout_bytecount_ ( SrbSize ) PSCSI_REQUEST_BLOCK Srb ,
_In_ ULONG SrbSize ,
_Out_ PCLASS_VPD_B0_DATA BlockLimitsData
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PVOID dataBuffer = NULL ;
UCHAR bufferLength = VPD_MAX_BUFFER_SIZE ; // use biggest buffer possible
ULONG allocationBufferLength = bufferLength ;
PCDB cdb ;
PVPD_BLOCK_LIMITS_PAGE blockLimits = NULL ;
ULONG dataTransferLength = 0 ;
//
// Set default values for UNMAP parameters based upon UNMAP support or lack
// thereof.
//
if ( FdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPU ) {
//
// If UNMAP is supported, we default to the maximum LBA count and
// block descriptor count. We also default the UNMAP granularity to
// a single block and specify no granularity alignment.
//
BlockLimitsData - > MaxUnmapLbaCount = ( ULONG ) - 1 ;
BlockLimitsData - > MaxUnmapBlockDescrCount = ( ULONG ) - 1 ;
BlockLimitsData - > OptimalUnmapGranularity = 1 ;
BlockLimitsData - > UnmapGranularityAlignment = 0 ;
BlockLimitsData - > UGAVALID = FALSE ;
} else {
BlockLimitsData - > MaxUnmapLbaCount = 0 ;
BlockLimitsData - > MaxUnmapBlockDescrCount = 0 ;
BlockLimitsData - > OptimalUnmapGranularity = 0 ;
BlockLimitsData - > UnmapGranularityAlignment = 0 ;
BlockLimitsData - > UGAVALID = FALSE ;
}
//
// Try to get the Block Limits VPD page (0xB0), if it is supported.
//
if ( FdoExtension - > FunctionSupportInfo - > ValidInquiryPages . BlockLimits = = TRUE )
{
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
allocationBufferLength = ALIGN_UP_BY ( allocationBufferLength , KeGetRecommendedSharedDataAlignment ( ) ) ;
dataBuffer = ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , allocationBufferLength , ' 0 CcS ' ) ;
# else
dataBuffer = ExAllocatePoolWithTag ( NonPagedPoolNx , bufferLength , ' 0 CcS ' ) ;
# endif
if ( dataBuffer = = NULL )
{
// return without updating FdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus
// the field will remain value as "-1", so that the command will be attempted next time this function is called.
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit ;
}
blockLimits = ( PVPD_BLOCK_LIMITS_PAGE ) dataBuffer ;
RtlZeroMemory ( dataBuffer , allocationBufferLength ) ;
if ( FdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) {
# ifdef _MSC_VER
# pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
# endif
status = InitializeStorageRequestBlock ( ( PSTORAGE_REQUEST_BLOCK ) Srb ,
STORAGE_ADDRESS_TYPE_BTL8 ,
SrbSize ,
1 ,
SrbExDataTypeScsiCdb16 ) ;
if ( NT_SUCCESS ( status ) ) {
( ( PSTORAGE_REQUEST_BLOCK ) Srb ) - > SrbFunction = SRB_FUNCTION_EXECUTE_SCSI ;
} else {
//
// Should not occur.
//
NT_ASSERT ( FALSE ) ;
}
} else {
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = SRB_FUNCTION_EXECUTE_SCSI ;
status = STATUS_SUCCESS ;
}
if ( NT_SUCCESS ( status ) ) {
// prepare the Srb
SrbSetTimeOutValue ( Srb , FdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , FdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , 6 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > CDB6INQUIRY3 . OperationCode = SCSIOP_INQUIRY ;
cdb - > CDB6INQUIRY3 . EnableVitalProductData = 1 ; //EVPD bit
cdb - > CDB6INQUIRY3 . PageCode = VPD_BLOCK_LIMITS ;
cdb - > CDB6INQUIRY3 . AllocationLength = bufferLength ; //AllocationLength field in CDB6INQUIRY3 is only one byte.
status = ClassSendSrbSynchronous ( FdoExtension - > DeviceObject ,
Srb ,
dataBuffer ,
allocationBufferLength ,
FALSE ) ;
dataTransferLength = SrbGetDataTransferLength ( Srb ) ;
}
//
// Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
// buffer was larger than necessary.
//
if ( status = = STATUS_DATA_OVERRUN & & dataTransferLength < bufferLength )
{
status = STATUS_SUCCESS ;
}
if ( NT_SUCCESS ( status ) )
{
USHORT pageLength ;
REVERSE_BYTES_SHORT ( & pageLength , & ( blockLimits - > PageLength ) ) ;
//
// Regardless of the device's support for unmap, cache away at least the basic block limits information
//
if ( dataTransferLength > = 0x10 & & blockLimits - > PageCode = = VPD_BLOCK_LIMITS ) {
// (6:7) OPTIMAL TRANSFER LENGTH GRANULARITY
REVERSE_BYTES_SHORT ( & BlockLimitsData - > OptimalTransferLengthGranularity , & blockLimits - > OptimalTransferLengthGranularity ) ;
// (8:11) MAXIMUM TRANSFER LENGTH
REVERSE_BYTES ( & BlockLimitsData - > MaximumTransferLength , & blockLimits - > MaximumTransferLength ) ;
// (12:15) OPTIMAL TRANSFER LENGTH
REVERSE_BYTES ( & BlockLimitsData - > OptimalTransferLength , & blockLimits - > OptimalTransferLength ) ;
}
if ( ( dataTransferLength < 0x24 ) | |
( pageLength < ( FIELD_OFFSET ( VPD_BLOCK_LIMITS_PAGE , Reserved1 ) - FIELD_OFFSET ( VPD_BLOCK_LIMITS_PAGE , Reserved0 ) ) ) | |
( blockLimits - > PageCode ! = VPD_BLOCK_LIMITS ) )
{
// the device should return at least 36 bytes of data for use.
// 'PageCode' shall match and we need all the relevant data after the header.
status = STATUS_INFO_LENGTH_MISMATCH ;
}
}
if ( NT_SUCCESS ( status ) )
{
// cache data into FdoExtension
// (20:23) MAXIMUM UNMAP LBA COUNT
REVERSE_BYTES ( & BlockLimitsData - > MaxUnmapLbaCount , & blockLimits - > MaximumUnmapLBACount ) ;
// (24:27) MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
REVERSE_BYTES ( & BlockLimitsData - > MaxUnmapBlockDescrCount , & blockLimits - > MaximumUnmapBlockDescriptorCount ) ;
// (28:31) OPTIMAL UNMAP GRANULARITY
REVERSE_BYTES ( & BlockLimitsData - > OptimalUnmapGranularity , & blockLimits - > OptimalUnmapGranularity ) ;
// (32:35) UNMAP GRANULARITY ALIGNMENT; (32) bit7: UGAVALID
BlockLimitsData - > UGAVALID = blockLimits - > UGAValid ;
if ( BlockLimitsData - > UGAVALID = = TRUE ) {
REVERSE_BYTES ( & BlockLimitsData - > UnmapGranularityAlignment , & blockLimits - > UnmapGranularityAlignment ) ;
BlockLimitsData - > UnmapGranularityAlignment & = 0x7FFFFFFF ; // remove value of UGAVALID bit.
} else {
BlockLimitsData - > UnmapGranularityAlignment = 0 ;
}
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_PNP ,
" ClasspDeviceGetBlockLimitsVPDPage (%p): %s %s (rev %s) reported following parameters: \
\ n \ t \ t \ tOptimalTransferLengthGranularity : % u \
\ n \ t \ t \ tMaximumTransferLength : % u \
\ n \ t \ t \ tOptimalTransferLength : % u \
\ n \ t \ t \ tMaximumUnmapLBACount : % u \
\ n \ t \ t \ tMaximumUnmapBlockDescriptorCount : % u \
\ n \ t \ t \ tOptimalUnmapGranularity : % u \
\ n \ t \ t \ tUGAValid : % u \
\ n \ t \ t \ tUnmapGranularityAlignment : % u \ n " ,
FdoExtension - > DeviceObject ,
( PCSZ ) ( ( ( PUCHAR ) FdoExtension - > DeviceDescriptor ) + FdoExtension - > DeviceDescriptor - > VendorIdOffset ) ,
( PCSZ ) ( ( ( PUCHAR ) FdoExtension - > DeviceDescriptor ) + FdoExtension - > DeviceDescriptor - > ProductIdOffset ) ,
( PCSZ ) ( ( ( PUCHAR ) FdoExtension - > DeviceDescriptor ) + FdoExtension - > DeviceDescriptor - > ProductRevisionOffset ) ,
BlockLimitsData - > OptimalTransferLengthGranularity ,
BlockLimitsData - > MaximumTransferLength ,
BlockLimitsData - > OptimalTransferLength ,
BlockLimitsData - > MaxUnmapLbaCount ,
BlockLimitsData - > MaxUnmapBlockDescrCount ,
BlockLimitsData - > OptimalUnmapGranularity ,
BlockLimitsData - > UGAVALID ,
BlockLimitsData - > UnmapGranularityAlignment ) ) ;
}
} else {
status = STATUS_INVALID_DEVICE_REQUEST ;
}
BlockLimitsData - > CommandStatus = status ;
Exit :
FREE_POOL ( dataBuffer ) ;
return status ;
}
NTSTATUS ClasspDeviceTrimProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
At first time of receiving the request , this function will forward it to lower stack to determine if it ' s supportted .
If it ' s not supported , INQUIRY ( Block Limits VPD page ) will be sent down to retrieve the information .
*/
{
NTSTATUS status = STATUS_SUCCESS ;
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSTORAGE_PROPERTY_QUERY query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
ULONG length = 0 ;
ULONG information = 0 ;
PDEVICE_TRIM_DESCRIPTOR trimDescr ;
UNREFERENCED_PARAMETER ( Srb ) ;
if ( ( DeviceObject - > DeviceType ! = FILE_DEVICE_DISK ) | |
( TEST_FLAG ( DeviceObject - > Characteristics , FILE_FLOPPY_DISKETTE ) ) | |
( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProperty = = Supported ) ) {
// if it's not disk, forward the request to lower layer,
// if the IOCTL is supported by lower stack, forward it down.
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
return status ;
}
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
status = STATUS_SUCCESS ;
goto Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
// do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
trimDescr = ( PDEVICE_TRIM_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length < sizeof ( DEVICE_TRIM_DESCRIPTOR ) ) {
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
trimDescr - > Version = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
trimDescr - > Size = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
goto Exit ;
}
status = STATUS_BUFFER_TOO_SMALL ;
goto Exit ;
}
//
// note that 'Supported' case has been handled at the beginning of this function.
//
switch ( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProperty ) {
case SupportUnknown : {
// send down request and wait for the request to complete.
status = ClassForwardIrpSynchronous ( commonExtension , Irp ) ;
if ( ( status = = STATUS_NOT_SUPPORTED ) | |
( status = = STATUS_NOT_IMPLEMENTED ) | |
( status = = STATUS_INVALID_DEVICE_REQUEST ) | |
( status = = STATUS_INVALID_PARAMETER_1 ) ) {
// case 1: the request is not supported by lower layer, sends down command
// some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
status = fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus ;
NT_ASSERT ( status ! = - 1 ) ;
// data is ready in fdoExtension
// set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProperty = NotSupported ;
if ( NT_SUCCESS ( status ) ) {
// fill output buffer
RtlZeroMemory ( trimDescr , length ) ;
trimDescr - > Version = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
trimDescr - > Size = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
trimDescr - > TrimEnabled = ClasspSupportsUnmap ( fdoExtension - > FunctionSupportInfo ) ;
// set returned data length
information = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
} else {
// there was error retrieving TrimProperty. Surface the error up from 'status' variable.
information = 0 ;
}
goto Exit ;
} else {
// case 2: the request is supported and it completes successfully
// case 3: the request is supported by lower stack but other failure status is returned.
// from now on, the same request will be send down to lower stack directly.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProperty = Supported ;
information = ( ULONG ) Irp - > IoStatus . Information ;
goto Exit ;
}
break ;
}
case NotSupported : {
status = fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus ;
NT_ASSERT ( status ! = - 1 ) ;
if ( NT_SUCCESS ( status ) ) {
RtlZeroMemory ( trimDescr , length ) ;
trimDescr - > Version = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
trimDescr - > Size = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
trimDescr - > TrimEnabled = ClasspSupportsUnmap ( fdoExtension - > FunctionSupportInfo ) ;
information = sizeof ( DEVICE_TRIM_DESCRIPTOR ) ;
} else {
information = 0 ;
}
goto Exit ;
break ;
}
case Supported : {
NT_ASSERT ( FALSE ) ; // this case is handled at the begining of the function.
break ;
}
} // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty)
Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
NTSTATUS ClasspDeviceLBProvisioningProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
{
NTSTATUS status = STATUS_SUCCESS ;
NTSTATUS blockLimitsStatus ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSTORAGE_PROPERTY_QUERY query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
ULONG length = 0 ;
ULONG information = 0 ;
CLASS_VPD_B0_DATA blockLimitsData ;
ULONG generationCount ;
PDEVICE_LB_PROVISIONING_DESCRIPTOR lbpDescr ;
UNREFERENCED_PARAMETER ( Srb ) ;
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
status = STATUS_SUCCESS ;
goto Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
lbpDescr = ( PDEVICE_LB_PROVISIONING_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
RtlZeroMemory ( lbpDescr , length ) ;
if ( length < DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE ) {
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
lbpDescr - > Version = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ;
lbpDescr - > Size = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
goto Exit ;
}
status = STATUS_BUFFER_TOO_SMALL ;
goto Exit ;
}
//
// Set the structure version/size based upon the size of the given output
// buffer. We may be working with an older component that was built with
// the V1 structure definition.
//
if ( length < sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ) {
lbpDescr - > Version = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE ;
lbpDescr - > Size = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE ;
information = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE ;
} else {
lbpDescr - > Version = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ;
lbpDescr - > Size = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ;
information = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ;
}
//
// Take a snapshot of the block limits data since it can change.
// If we failed to get the block limits data, we'll just set the Optimal
// Unmap Granularity (and alignment) will default to 0. We don't want to
// fail the request outright since there is some non-block limits data that
// we can return.
//
blockLimitsStatus = ClasspBlockLimitsDataSnapshot ( fdoExtension ,
TRUE ,
& blockLimitsData ,
& generationCount ) ;
//
// Fill in the output buffer. All data is copied from the FDO extension where we
// cached Logical Block Provisioning info when the device was first initialized.
//
lbpDescr - > ThinProvisioningEnabled = ClasspIsThinProvisioned ( fdoExtension - > FunctionSupportInfo ) ;
//
// Make sure we have a non-zero value for the number of bytes per block.
//
if ( fdoExtension - > DiskGeometry . BytesPerSector = = 0 )
{
status = ClassReadDriveCapacity ( fdoExtension - > DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) | | fdoExtension - > DiskGeometry . BytesPerSector = = 0 )
{
status = STATUS_INVALID_DEVICE_REQUEST ;
information = 0 ;
goto Exit ;
}
}
lbpDescr - > ThinProvisioningReadZeros = fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPRZ ;
lbpDescr - > AnchorSupported = fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ANC_SUP ;
if ( NT_SUCCESS ( blockLimitsStatus ) ) {
lbpDescr - > UnmapGranularityAlignmentValid = blockLimitsData . UGAVALID ;
//
// Granularity and Alignment are given to us in units of blocks,
// but we convert and return them in bytes as it is more convenient
// to the caller.
//
lbpDescr - > OptimalUnmapGranularity = ( ULONGLONG ) blockLimitsData . OptimalUnmapGranularity * fdoExtension - > DiskGeometry . BytesPerSector ;
lbpDescr - > UnmapGranularityAlignment = ( ULONGLONG ) blockLimitsData . UnmapGranularityAlignment * fdoExtension - > DiskGeometry . BytesPerSector ;
# if (NTDDI_VERSION >= NTDDI_WINBLUE)
//
// If the output buffer is large enough (i.e. not a V1 structure) copy
// over the max UNMAP LBA count and max UNMAP block descriptor count.
//
if ( length > = sizeof ( DEVICE_LB_PROVISIONING_DESCRIPTOR ) ) {
lbpDescr - > MaxUnmapLbaCount = blockLimitsData . MaxUnmapLbaCount ;
lbpDescr - > MaxUnmapBlockDescriptorCount = blockLimitsData . MaxUnmapBlockDescrCount ;
}
# endif
}
Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
VOID
ConvertDataSetRangeToUnmapBlockDescr (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_ PUNMAP_BLOCK_DESCRIPTOR BlockDescr ,
_Inout_ PULONG CurrentBlockDescrIndex ,
_In_ ULONG MaxBlockDescrIndex ,
_Inout_ PULONGLONG CurrentLbaCount ,
_In_ ULONGLONG MaxLbaCount ,
_Inout_ PDEVICE_DATA_SET_RANGE DataSetRange
)
/*++
Routine Description :
Convert DEVICE_DATA_SET_RANGE entry to be UNMAP_BLOCK_DESCRIPTOR entries .
As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits ( bytes )
and LbaCount field in UNMAP_BLOCK_DESCRIPTOR structure is 32 bits ( sectors ) ,
it ' s possible that one DEVICE_DATA_SET_RANGE entry needs multiple UNMAP_BLOCK_DESCRIPTOR entries .
We must also take the unmap granularity into consideration and split up the
the given ranges so that they are aligned with the specified granularity .
Arguments :
All arguments must be validated by the caller .
FdoExtension - The FDO extension of the device to which the unmap
command that will use the resulting unmap block descriptors will be
sent .
BlockDescr - Pointer to a buffer that will contain the unmap block
descriptors . This buffer should be allocated by the caller and the
caller should also ensure that it is large enough to contain all the
requested descriptors . Its size is implied by MaxBlockDescrIndex .
CurrentBlockDescrIndex - This contains the next block descriptor index to
be processed when this function returns . This function should be called
again with the same parameter to continue processing .
MaxBlockDescrIndex - This is the index of the last unmap block descriptor ,
provided so that the function does not go off the end of BlockDescr .
CurrentLbaCount - This contains the number of LBAs left to be processed
when this function returns . This function should be called again with
the same parameter to continue processing .
MaxLbaCount - This is the max number of LBAs that can be sent in a single
unmap command .
DataSetRange - This range will be modified to reflect the un - converted part .
It must be valid ( including being granularity - aligned ) when it is first
passed to this function .
Return Value :
Count of UNMAP_BLOCK_DESCRIPTOR entries converted .
NOTE : if LengthInBytes does not reach to 0 , the conversion for DEVICE_DATA_SET_RANGE entry
is not completed . Further conversion is needed by calling this function again .
- - */
{
ULONGLONG startingSector ;
ULONGLONG sectorCount ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" ConvertDataSetRangeToUnmapBlockDescr (%p): Generating UNMAP Block Descriptors from DataSetRange: \
\ n \ t \ tStartingOffset = % I64u bytes \
\ n \ t \ tLength = % I64u bytes \ n " ,
FdoExtension - > DeviceObject ,
DataSetRange - > StartingOffset ,
DataSetRange - > LengthInBytes ) ) ;
while ( ( DataSetRange - > LengthInBytes > 0 ) & &
( * CurrentBlockDescrIndex < MaxBlockDescrIndex ) & &
( * CurrentLbaCount < MaxLbaCount ) ) {
//
// Convert the starting offset and length from bytes to blocks.
//
startingSector = ( ULONGLONG ) ( DataSetRange - > StartingOffset / FdoExtension - > DiskGeometry . BytesPerSector ) ;
sectorCount = ( DataSetRange - > LengthInBytes / FdoExtension - > DiskGeometry . BytesPerSector ) ;
//
// Make sure the sector count isn't more than can be specified with a
// single descriptor.
//
if ( sectorCount > MAXULONG ) {
sectorCount = MAXULONG ;
}
//
// The max LBA count is the max number of LBAs that can be unmapped with
// a single UNMAP command. Make sure we don't exceed this value.
//
if ( ( * CurrentLbaCount + sectorCount ) > MaxLbaCount ) {
sectorCount = MaxLbaCount - * CurrentLbaCount ;
}
REVERSE_BYTES_QUAD ( BlockDescr [ * CurrentBlockDescrIndex ] . StartingLba , & startingSector ) ;
REVERSE_BYTES ( BlockDescr [ * CurrentBlockDescrIndex ] . LbaCount , ( PULONG ) & sectorCount ) ;
DataSetRange - > StartingOffset + = sectorCount * FdoExtension - > DiskGeometry . BytesPerSector ;
DataSetRange - > LengthInBytes - = sectorCount * FdoExtension - > DiskGeometry . BytesPerSector ;
* CurrentBlockDescrIndex + = 1 ;
* CurrentLbaCount + = ( ULONG ) sectorCount ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" ConvertDataSetRangeToUnmapBlockDescr (%p): Generated UNMAP Block Descriptor: \
\ n \ t \ t \ tStartingLBA = % I64u \
\ n \ t \ t \ tLBACount = % I64u \ n " ,
FdoExtension - > DeviceObject ,
startingSector ,
sectorCount ) ) ;
}
return ;
}
NTSTATUS
DeviceProcessDsmTrimRequest (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_ PDEVICE_DATA_SET_RANGE DataSetRanges ,
_In_ ULONG DataSetRangesCount ,
_In_ ULONG UnmapGranularity ,
_In_ ULONG SrbFlags ,
_In_ PIRP Irp ,
_In_ PGUID ActivityId ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description :
Process TRIM request that received from upper layer .
Arguments :
FdoExtension
DataSetRanges - this parameter must be already validated in caller .
DataSetRangesCount - this parameter must be already validated in caller .
UnmapGranularity - The unmap granularity in blocks . This is used to split
up the unmap command into chunks that are granularity - aligned .
Srb - The SRB to use for the unmap command . The caller must allocate it ,
but this function will take care of initialzing it .
Return Value :
status of the operation
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
PUNMAP_LIST_HEADER buffer = NULL ;
PUNMAP_BLOCK_DESCRIPTOR blockDescrPointer ;
ULONG bufferLength ;
ULONG maxBlockDescrCount ;
ULONG neededBlockDescrCount ;
ULONG i ;
BOOLEAN allDataSetRangeFullyConverted ;
BOOLEAN needToSendCommand ;
BOOLEAN tempDataSetRangeFullyConverted ;
ULONG dataSetRangeIndex ;
DEVICE_DATA_SET_RANGE tempDataSetRange ;
ULONG blockDescrIndex ;
ULONGLONG lbaCount ;
ULONGLONG maxLbaCount ;
ULONGLONG maxParameterListLength ;
UNREFERENCED_PARAMETER ( UnmapGranularity ) ;
UNREFERENCED_PARAMETER ( ActivityId ) ;
UNREFERENCED_PARAMETER ( Irp ) ;
//
// The given LBA ranges are in DEVICE_DATA_SET_RANGE format and need to be converted into UNMAP Block Descriptors.
// The UNMAP command is able to carry 0xFFFF bytes (0xFFF8 in reality as there are 8 bytes of header plus n*16 bytes of Block Descriptors) of data.
// The actual size will also be constrained by the Maximum LBA Count and Maximum Transfer Length.
//
//
// 1.1 Calculate how many Block Descriptors are needed to complete this request.
//
neededBlockDescrCount = 0 ;
for ( i = 0 ; i < DataSetRangesCount ; i + + ) {
lbaCount = DataSetRanges [ i ] . LengthInBytes / FdoExtension - > DiskGeometry . BytesPerSector ;
//
// 1.1.1 the UNMAP_BLOCK_DESCRIPTOR LbaCount is 32 bits, the max value is 0xFFFFFFFF
//
if ( lbaCount > 0 ) {
neededBlockDescrCount + = ( ULONG ) ( ( lbaCount - 1 ) / MAXULONG + 1 ) ;
}
}
//
// Honor Max Unmap Block Descriptor Count if it has been specified. Otherwise,
// use the maximum value that the Parameter List Length field will allow (0xFFFF).
// If the count is 0xFFFFFFFF, then no maximum is specified.
//
if ( FdoExtension - > FunctionSupportInfo - > BlockLimitsData . MaxUnmapBlockDescrCount ! = 0 & &
FdoExtension - > FunctionSupportInfo - > BlockLimitsData . MaxUnmapBlockDescrCount ! = MAXULONG )
{
maxParameterListLength = ( ULONGLONG ) ( FdoExtension - > FunctionSupportInfo - > BlockLimitsData . MaxUnmapBlockDescrCount * sizeof ( UNMAP_BLOCK_DESCRIPTOR ) )
+ sizeof ( UNMAP_LIST_HEADER ) ;
//
// In the SBC-3, the Max Unmap Block Descriptor Count field in the 0xB0
// page is 4 bytes and the Parameter List Length in the UNMAP command is
// 2 bytes, therefore it is possible that the Max Unmap Block Descriptor
// Count could imply more bytes than can be specified in the Parameter
// List Length field. Adjust for that here.
//
maxParameterListLength = min ( maxParameterListLength , MAXUSHORT ) ;
}
else
{
maxParameterListLength = MAXUSHORT ;
}
//
// 1.2 Calculate the buffer size needed, capped by the device's limitations.
//
bufferLength = min ( FdoExtension - > PrivateFdoData - > HwMaxXferLen , ( ULONG ) maxParameterListLength ) ;
bufferLength = min ( bufferLength , ( neededBlockDescrCount * sizeof ( UNMAP_BLOCK_DESCRIPTOR ) + sizeof ( UNMAP_LIST_HEADER ) ) ) ;
maxBlockDescrCount = ( bufferLength - sizeof ( UNMAP_LIST_HEADER ) ) / sizeof ( UNMAP_BLOCK_DESCRIPTOR ) ;
if ( maxBlockDescrCount = = 0 ) {
//
// This shouldn't happen since we've already done validation.
//
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" DeviceProcessDsmTrimRequest (%p): Max Block Descriptor count is Zero \n " ,
FdoExtension - > DeviceObject ) ) ;
NT_ASSERT ( maxBlockDescrCount ! = 0 ) ;
status = STATUS_DATA_ERROR ;
goto Exit ;
}
//
// The Maximum LBA Count is set during device initialization.
//
maxLbaCount = ( ULONGLONG ) FdoExtension - > FunctionSupportInfo - > BlockLimitsData . MaxUnmapLbaCount ;
if ( maxLbaCount = = 0 ) {
//
// This shouldn't happen since we've already done validation.
//
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" DeviceProcessDsmTrimRequest (%p): Max LBA count is Zero \n " ,
FdoExtension - > DeviceObject ) ) ;
NT_ASSERT ( maxLbaCount ! = 0 ) ;
status = STATUS_DATA_ERROR ;
goto Exit ;
}
//
// Finally, allocate the buffer we'll use to send the UNMAP command.
//
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
bufferLength = ALIGN_UP_BY ( bufferLength , KeGetRecommendedSharedDataAlignment ( ) ) ;
buffer = ( PUNMAP_LIST_HEADER ) ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , bufferLength , CLASS_TAG_LB_PROVISIONING ) ;
# else
buffer = ( PUNMAP_LIST_HEADER ) ExAllocatePoolWithTag ( NonPagedPoolNx , bufferLength , CLASS_TAG_LB_PROVISIONING ) ;
# endif
if ( buffer = = NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit ;
}
RtlZeroMemory ( buffer , bufferLength ) ;
blockDescrPointer = & buffer - > Descriptors [ 0 ] ;
allDataSetRangeFullyConverted = FALSE ;
needToSendCommand = FALSE ;
tempDataSetRangeFullyConverted = TRUE ;
dataSetRangeIndex = 0 ;
RtlZeroMemory ( & tempDataSetRange , sizeof ( tempDataSetRange ) ) ;
blockDescrIndex = 0 ;
lbaCount = 0 ;
while ( ! allDataSetRangeFullyConverted ) {
//
// If the previous entry conversion completed, go on to the next one;
// otherwise, continue processing the current entry.
//
if ( tempDataSetRangeFullyConverted ) {
tempDataSetRange . StartingOffset = DataSetRanges [ dataSetRangeIndex ] . StartingOffset ;
tempDataSetRange . LengthInBytes = DataSetRanges [ dataSetRangeIndex ] . LengthInBytes ;
dataSetRangeIndex + + ;
}
ConvertDataSetRangeToUnmapBlockDescr ( FdoExtension ,
blockDescrPointer ,
& blockDescrIndex ,
maxBlockDescrCount ,
& lbaCount ,
maxLbaCount ,
& tempDataSetRange
) ;
tempDataSetRangeFullyConverted = ( tempDataSetRange . LengthInBytes = = 0 ) ? TRUE : FALSE ;
allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted & & ( dataSetRangeIndex = = DataSetRangesCount ) ;
//
// Send the UNMAP command when the buffer is full or when all input entries are converted.
//
if ( ( blockDescrIndex = = maxBlockDescrCount ) | | // Buffer full or block descriptor count reached
( lbaCount = = maxLbaCount ) | | // Block LBA count reached
allDataSetRangeFullyConverted ) { // All DataSetRanges have been converted
USHORT transferSize ;
USHORT tempSize ;
PCDB cdb ;
//
// Get the transfer size, including the header.
//
transferSize = ( USHORT ) ( blockDescrIndex * sizeof ( UNMAP_BLOCK_DESCRIPTOR ) + sizeof ( UNMAP_LIST_HEADER ) ) ;
if ( transferSize > bufferLength )
{
//
// This should never happen.
//
NT_ASSERT ( transferSize < = bufferLength ) ;
status = STATUS_BUFFER_TOO_SMALL ;
break ;
}
tempSize = transferSize - ( USHORT ) FIELD_OFFSET ( UNMAP_LIST_HEADER , BlockDescrDataLength ) ;
REVERSE_BYTES_SHORT ( buffer - > DataLength , & tempSize ) ;
tempSize = transferSize - ( USHORT ) FIELD_OFFSET ( UNMAP_LIST_HEADER , Descriptors [ 0 ] ) ;
REVERSE_BYTES_SHORT ( buffer - > BlockDescrDataLength , & tempSize ) ;
//
// Initialize the SRB.
//
if ( FdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) {
status = InitializeStorageRequestBlock ( ( PSTORAGE_REQUEST_BLOCK ) Srb ,
STORAGE_ADDRESS_TYPE_BTL8 ,
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE ,
1 ,
SrbExDataTypeScsiCdb16 ) ;
if ( NT_SUCCESS ( status ) ) {
( ( PSTORAGE_REQUEST_BLOCK ) Srb ) - > SrbFunction = SRB_FUNCTION_EXECUTE_SCSI ;
} else {
//
// Should not occur.
//
NT_ASSERT ( FALSE ) ;
break ;
}
} else {
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = SRB_FUNCTION_EXECUTE_SCSI ;
}
//
// Prepare the Srb
//
SrbSetTimeOutValue ( Srb , FdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
//
// Set the SrbFlags to indicate that it's a data-out operation.
// Also set any passed-in SrbFlags.
//
SrbAssignSrbFlags ( Srb , FdoExtension - > SrbFlags ) ;
SrbClearSrbFlags ( Srb , SRB_FLAGS_DATA_IN ) ;
SrbSetSrbFlags ( Srb , SRB_FLAGS_DATA_OUT ) ;
SrbSetSrbFlags ( Srb , SrbFlags ) ;
SrbSetCdbLength ( Srb , 10 ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > UNMAP . OperationCode = SCSIOP_UNMAP ;
cdb - > UNMAP . Anchor = 0 ;
cdb - > UNMAP . GroupNumber = 0 ;
cdb - > UNMAP . AllocationLength [ 0 ] = ( UCHAR ) ( transferSize > > 8 ) ;
cdb - > UNMAP . AllocationLength [ 1 ] = ( UCHAR ) transferSize ;
status = ClassSendSrbSynchronous ( FdoExtension - > DeviceObject ,
Srb ,
buffer ,
transferSize ,
TRUE ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" DeviceProcessDsmTrimRequest (%p): UNMAP command issued. Returned NTSTATUS: %!STATUS!. \n " ,
FdoExtension - > DeviceObject ,
status
) ) ;
//
// Clear the buffer so we can re-use it.
//
blockDescrIndex = 0 ;
lbaCount = 0 ;
RtlZeroMemory ( buffer , bufferLength ) ;
}
}
Exit :
FREE_POOL ( buffer ) ;
return status ;
}
NTSTATUS ClasspDeviceTrimProcess (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_In_ PGUID ActivityId ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Trim .
At first time of receiving the request , this function will forward it to lower stack to determine if it ' s supportted .
If it ' s not supported , UNMAP ( with anchor attribute set ) will be sent down to process the request .
*/
{
NTSTATUS status = STATUS_SUCCESS ;
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp - > AssociatedIrp . SystemBuffer ;
PDEVICE_DATA_SET_RANGE dataSetRanges ;
ULONG dataSetRangesCount ;
DEVICE_DATA_SET_RANGE entireDataSetRange = { 0 } ;
ULONG i ;
ULONGLONG granularityAlignmentInBytes ;
ULONG granularityInBlocks ;
ULONG srbFlags = 0 ;
CLASS_VPD_B0_DATA blockLimitsData ;
ULONG generationCount ;
if ( ( DeviceObject - > DeviceType ! = FILE_DEVICE_DISK ) | |
( TEST_FLAG ( DeviceObject - > Characteristics , FILE_FLOPPY_DISKETTE ) ) | |
( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProcess = = Supported ) ) {
// if it's not disk, forward the request to lower layer,
// if the IOCTL is supported by lower stack, forward it down.
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspDeviceTrimProcess (%p): Lower layer supports Trim DSM IOCTL, forwarding IOCTL. \n " ,
DeviceObject ) ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
return status ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
//
// If the caller has not set the "entire dataset range" flag then at least
// one dataset range should be specified. However, if the caller *has* set
// the flag, then there should not be any dataset ranges specified.
//
if ( ( ! TEST_FLAG ( dsmAttributes - > Flags , DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE ) & &
( dsmAttributes - > DataSetRangesOffset = = 0 | |
dsmAttributes - > DataSetRangesLength = = 0 ) ) | |
( TEST_FLAG ( dsmAttributes - > Flags , DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE ) & &
( dsmAttributes - > DataSetRangesOffset ! = 0 | |
dsmAttributes - > DataSetRangesLength ! = 0 ) ) ) {
status = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
//
// note that 'Supported' case has been handled at the beginning of this function.
//
switch ( fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProcess ) {
case SupportUnknown : {
// send down request and wait for the request to complete.
status = ClassForwardIrpSynchronous ( commonExtension , Irp ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspDeviceTrimProcess (%p): Trim DSM IOCTL support unknown. Forwarded IOCTL and received NTSTATUS %!STATUS!. \n " ,
DeviceObject ,
status ) ) ;
if ( ClasspLowerLayerNotSupport ( status ) ) {
// case 1: the request is not supported by lower layer, sends down command
// some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
// In this case we'll just fall through to the NotSupported case so that we can handle it ourselves.
//
// VPD pages 0xB2 and 0xB0 should have been cached in Start Device phase - ClassPnpStartDevice.
// 0xB2 page: fdoExtension->FunctionSupportInfo->LBProvisioningData;
// 0xB0 page: fdoExtension->FunctionSupportInfo->BlockLimitsData
//
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . LBProvisioning = = TRUE ) {
NT_ASSERT ( fdoExtension - > FunctionSupportInfo - > LBProvisioningData . CommandStatus ! = - 1 ) ;
}
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . BlockLimits = = TRUE ) {
NT_ASSERT ( fdoExtension - > FunctionSupportInfo - > BlockLimitsData . CommandStatus ! = - 1 ) ;
}
} else {
// case 2: the request is supported and it completes successfully
// case 3: the request is supported by lower stack but other failure status is returned.
// from now on, the same request will be send down to lower stack directly.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProcess = Supported ;
goto Exit ;
}
}
case NotSupported : {
// send UNMAP command if it is supported. don't need to check 'status' value.
if ( ClasspSupportsUnmap ( fdoExtension - > FunctionSupportInfo ) )
{
//
// Make sure that we know the bytes per sector (logical block) as it's
// necessary for calculations involving granularity and alignment.
//
if ( fdoExtension - > DiskGeometry . BytesPerSector = = 0 ) {
status = ClassReadDriveCapacity ( fdoExtension - > DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) | | fdoExtension - > DiskGeometry . BytesPerSector = = 0 ) {
status = STATUS_INVALID_DEVICE_REQUEST ;
goto Exit ;
}
}
//
// Take a snapshot of the block limits data since it can change.
// It's acceptable if the block limits data is outdated since
// there isn't a hard requirement on the unmap granularity.
//
ClasspBlockLimitsDataSnapshot ( fdoExtension ,
FALSE ,
& blockLimitsData ,
& generationCount ) ;
//
// Check to see if the Optimal Unmap Granularity and Unmap Granularity
// Alignment have been specified. If not, default the granularity to
// one block and the alignment to zero.
//
if ( blockLimitsData . OptimalUnmapGranularity ! = 0 )
{
granularityInBlocks = blockLimitsData . OptimalUnmapGranularity ;
}
else
{
granularityInBlocks = 1 ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspDeviceTrimProcess (%p): Optimal Unmap Granularity not provided, defaulted to 1. \n " ,
DeviceObject ) ) ;
}
if ( blockLimitsData . UGAVALID = = TRUE )
{
granularityAlignmentInBytes = ( ULONGLONG ) blockLimitsData . UnmapGranularityAlignment * fdoExtension - > DiskGeometry . BytesPerSector ;
}
else
{
granularityAlignmentInBytes = 0 ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspDeviceTrimProcess (%p): Unmap Granularity Alignment not provided, defaulted to 0. \n " ,
DeviceObject ) ) ;
}
if ( TEST_FLAG ( dsmAttributes - > Flags , DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE ) )
{
//
// The caller wants to UNMAP the entire disk so we need to build a single
// dataset range that represents the entire disk.
//
entireDataSetRange . StartingOffset = granularityAlignmentInBytes ;
entireDataSetRange . LengthInBytes = ( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart - ( ULONGLONG ) entireDataSetRange . StartingOffset ;
dataSetRanges = & entireDataSetRange ;
dataSetRangesCount = 1 ;
}
else
{
dataSetRanges = ( PDEVICE_DATA_SET_RANGE ) ( ( PUCHAR ) dsmAttributes + dsmAttributes - > DataSetRangesOffset ) ;
dataSetRangesCount = dsmAttributes - > DataSetRangesLength / sizeof ( DEVICE_DATA_SET_RANGE ) ;
//
// Validate the data ranges. Make sure the range is block-aligned,
// falls in a valid portion of the disk, and is non-zero.
//
for ( i = 0 ; i < dataSetRangesCount ; i + + )
{
if ( ( dataSetRanges [ i ] . StartingOffset % fdoExtension - > DiskGeometry . BytesPerSector ! = 0 ) | |
( dataSetRanges [ i ] . LengthInBytes % fdoExtension - > DiskGeometry . BytesPerSector ! = 0 ) | |
( dataSetRanges [ i ] . StartingOffset < ( LONGLONG ) granularityAlignmentInBytes ) | |
( dataSetRanges [ i ] . LengthInBytes = = 0 ) | |
( ( ULONGLONG ) dataSetRanges [ i ] . StartingOffset + dataSetRanges [ i ] . LengthInBytes > ( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart ) )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceTrimProcess (%p): Invalid dataset range. StartingOffset = %I64x, LengthInBytes = %I64x \n " ,
DeviceObject ,
dataSetRanges [ i ] . StartingOffset ,
dataSetRanges [ i ] . LengthInBytes ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
}
}
if ( ! TEST_FLAG ( dsmAttributes - > Flags , DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED ) )
{
{
//
// For security reasons, file-level TRIM must be forwarded on only
// if reading the unmapped blocks' contents will return back zeros.
// This is because if LBPRZ bit is not set, it indicates that a read
// of unmapped blocks may return "any" data thus potentially leaking
// in data (into the read buffer) from other blocks.
//
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . LBProvisioning & &
! fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPRZ ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceTrimProcess (%p): Device does not support file level TRIM. \n " ,
DeviceObject ) ) ;
status = STATUS_TRIM_READ_ZERO_NOT_SUPPORTED ;
goto Exit ;
}
}
}
// process DSM IOCTL
status = DeviceProcessDsmTrimRequest ( fdoExtension ,
dataSetRanges ,
dataSetRangesCount ,
granularityInBlocks ,
srbFlags ,
Irp ,
ActivityId ,
Srb ) ;
} else {
// DSM IOCTL should be completed as not supported
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceTrimProcess (%p): Device does not support UNMAP. \n " ,
DeviceObject ) ) ;
status = STATUS_NOT_SUPPORTED ;
}
// set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
fdoExtension - > FunctionSupportInfo - > LowerLayerSupport . TrimProcess = NotSupported ;
break ;
}
case Supported : {
NT_ASSERT ( FALSE ) ; // this case is handled at the begining of the function.
break ;
}
} // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess)
Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = 0 ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
NTSTATUS
GetLBAStatus (
_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ,
_In_ PSCSI_REQUEST_BLOCK Srb ,
_In_ ULONGLONG StartingLBA ,
_Inout_ PLBA_STATUS_LIST_HEADER LBAStatusHeader ,
_In_ ULONG LBAStatusSize ,
_In_ BOOLEAN ConsolidateableBlocksOnly
)
/*++
Routine Description :
Send down a Get LBA Status command for the given range .
Arguments :
FdoExtension : The FDO extension of the device to which Get LBA Status will
be sent .
Srb : This should be allocated and initialized before it ' s passed in . It
will be used for the Get LBA Status command .
StartingLBA : The LBA that is at the beginning of the requested range .
LBAStatusHeader : Caller - allocated output buffer .
LBASTatusSize : Size of the caller - allocated output buffer .
Return Value :
Status of the operation .
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
PCDB cdb ;
if ( LBAStatusHeader = = NULL | | LBAStatusSize = = 0 )
{
return STATUS_INVALID_PARAMETER ;
}
//
// Build and send down the Get LBA Status command.
//
SrbSetTimeOutValue ( Srb , FdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , FdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , sizeof ( cdb - > GET_LBA_STATUS ) ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > GET_LBA_STATUS . OperationCode = SCSIOP_GET_LBA_STATUS ;
cdb - > GET_LBA_STATUS . ServiceAction = SERVICE_ACTION_GET_LBA_STATUS ;
REVERSE_BYTES_QUAD ( & ( cdb - > GET_LBA_STATUS . StartingLBA ) , & StartingLBA ) ;
REVERSE_BYTES ( & ( cdb - > GET_LBA_STATUS . AllocationLength ) , & LBAStatusSize ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" GetLBAStatus (%p): sending command with StartingLBA = 0x%I64x, AllocationLength = 0x%I64x, ConsolidateableBlocksOnly = %u \n " ,
FdoExtension - > DeviceObject ,
StartingLBA ,
LBAStatusSize ,
ConsolidateableBlocksOnly ) ) ;
status = ClassSendSrbSynchronous ( FdoExtension - > DeviceObject ,
Srb ,
LBAStatusHeader ,
LBAStatusSize ,
FALSE ) ;
//
// Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
// buffer was larger than necessary.
//
if ( status = = STATUS_DATA_OVERRUN & &
SrbGetDataTransferLength ( Srb ) < LBAStatusSize )
{
status = STATUS_SUCCESS ;
}
// log command.
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" GetLBAStatus (%p): command returned NT Status: %!STATUS! \n " ,
FdoExtension - > DeviceObject ,
status
) ) ;
return status ;
}
NTSTATUS ClasspDeviceGetLBAStatus (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
Routine Description :
This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation .
1. This function will only handle the first dataset range .
2. This function will not handle dataset ranges whose LengthInBytes is greater than :
( ( MAXULONG - sizeof ( LBA_STATUS_LIST_HEADER ) ) / sizeof ( LBA_STATUS_DESCRIPTOR ) ) * BytesPerSlab
The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
of slabs for which mapping status is desired .
The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
contains a bitmap that represents the mapped status of the slabs in the requested
range . Note that the number of slabs returned may be less than the number
requested .
Thus function will automatically re - align the given range offset if it was
not slab - aligned . The delta between the given range offset and the properly
aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE .
Arguments :
DeviceObject : The FDO of the device to which Get LBA Status will be sent .
Irp : The IRP for the request . This function will read the input buffer and
write to the output buffer at the current IRP stack location .
Srb : This should be allocated and initialized before it ' s passed in . It
will be used for the Get LBA Status command .
Return Value :
STATUS_INVALID_PARAMETER : May be returned under the following conditions :
- If the requested range was too large . The caller should try again with a
smaller range . See above for how to calculate the maximum range .
- If the given starting offset was not within the valid range of the device .
STATUS_NOT_SUPPORTED : The storage did not report some information critical to
the execution of this function ( e . g . Optimal Unmap Granularity ) .
STATUS_BUFFER_TOO_SMALL : The output buffer is not large enough to hold the max
data that could be returned from this function . If the output buffer is
at least the size of a ULONG , we will write the required output buffer size
to the first ULONG bytes of the output buffer .
STATUS_UNSUCCESSFUL : The Get LBA Status command succeeded but did not
return data as expected .
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = ( PDEVICE_MANAGE_DATA_SET_ATTRIBUTES ) Irp - > AssociatedIrp . SystemBuffer ;
PDEVICE_DATA_SET_RANGE dataSetRanges = NULL ;
PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT dsmOutput = ( PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT ) Irp - > AssociatedIrp . SystemBuffer ;
ULONG dsmOutputLength ;
NTSTATUS finalStatus ;
NTSTATUS getLBAWorkerStatus ;
ULONG retryCount ;
ULONG retryCountMax ;
CLASS_VPD_B0_DATA blockLimitsData ;
ULONG generationCount1 ;
ULONG generationCount2 ;
BOOLEAN blockLimitsDataMayHaveChanged ;
ULONG_PTR information = 0 ;
LONGLONG startingOffset ;
ULONGLONG lengthInBytes ;
BOOLEAN consolidateableBlocksOnly = FALSE ;
ULONG outputVersion ;
//
// Basic parameter validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( dsmOutput = = NULL | |
dsmAttributes = = NULL )
{
finalStatus = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
if ( TEST_FLAG ( dsmAttributes - > Flags , DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE ) ) {
//
// The caller wants the mapping status of the entire disk.
//
ULONG unmapGranularityAlignment = 0 ;
if ( fdoExtension - > FunctionSupportInfo - > BlockLimitsData . UGAVALID ) {
unmapGranularityAlignment = fdoExtension - > FunctionSupportInfo - > BlockLimitsData . UnmapGranularityAlignment ;
}
startingOffset = unmapGranularityAlignment ;
lengthInBytes = ( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart - ( ULONGLONG ) startingOffset ;
} else {
if ( dsmAttributes - > DataSetRangesOffset = = 0 | |
dsmAttributes - > DataSetRangesLength = = 0 ) {
finalStatus = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
//
// We only service the first dataset range specified.
//
dataSetRanges = ( PDEVICE_DATA_SET_RANGE ) ( ( PUCHAR ) dsmAttributes + dsmAttributes - > DataSetRangesOffset ) ;
startingOffset = dataSetRanges [ 0 ] . StartingOffset ;
lengthInBytes = dataSetRanges [ 0 ] . LengthInBytes ;
}
//
// See if the sender is requesting a specific version of the output data
// structure. Othwerwise, default to V1.
//
outputVersion = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1 ;
# if (NTDDI_VERSION >= NTDDI_WINBLUE)
if ( ( dsmAttributes - > ParameterBlockOffset > = sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES ) ) & &
( dsmAttributes - > ParameterBlockLength > = sizeof ( DEVICE_DATA_SET_LBP_STATE_PARAMETERS ) ) ) {
PDEVICE_DATA_SET_LBP_STATE_PARAMETERS parameters = Add2Ptr ( dsmAttributes , dsmAttributes - > ParameterBlockOffset ) ;
if ( ( parameters - > Version = = DEVICE_DATA_SET_LBP_STATE_PARAMETERS_VERSION_V1 ) & &
( parameters - > Size > = sizeof ( DEVICE_DATA_SET_LBP_STATE_PARAMETERS ) ) ) {
outputVersion = parameters - > OutputVersion ;
if ( ( outputVersion ! = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1 ) & &
( outputVersion ! = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2 ) ) {
finalStatus = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
}
}
# endif
//
// Take a snapshot of the block limits data for the worker function to use.
// We need to fail the request if we fail to get updated block limits data
// since we need an accurate Optimal Unmap Granularity value to properly
// convert the returned mapping descriptors into a bitmap.
//
finalStatus = ClasspBlockLimitsDataSnapshot ( fdoExtension ,
TRUE ,
& blockLimitsData ,
& generationCount1 ) ;
if ( ! NT_SUCCESS ( finalStatus ) ) {
information = 0 ;
goto Exit ;
}
if ( dsmAttributes - > Flags & DEVICE_DSM_FLAG_ALLOCATION_CONSOLIDATEABLE_ONLY ) {
consolidateableBlocksOnly = TRUE ;
}
//
// The retry logic is to handle the case when block limits data changes during rare occasions
// (e.g. diff-VHD fork or merge).
//
retryCountMax = GET_LBA_STATUS_RETRY_COUNT_MAX ;
for ( retryCount = 0 ; retryCount < retryCountMax ; retryCount + + ) {
dsmOutputLength = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
getLBAWorkerStatus = ClasspDeviceGetLBAStatusWorker ( DeviceObject ,
& blockLimitsData ,
startingOffset ,
lengthInBytes ,
dsmOutput ,
& dsmOutputLength ,
Srb ,
consolidateableBlocksOnly ,
outputVersion ,
& blockLimitsDataMayHaveChanged ) ;
if ( ! NT_SUCCESS ( getLBAWorkerStatus ) & & ! blockLimitsDataMayHaveChanged ) {
information = 0 ;
finalStatus = getLBAWorkerStatus ;
break ;
}
//
// Again, we need to fail the request if we fail to get updated block
// limits data since we need an accurate Optimal Unmap Granularity value.
//
finalStatus = ClasspBlockLimitsDataSnapshot ( fdoExtension ,
TRUE ,
& blockLimitsData ,
& generationCount2 ) ;
if ( ! NT_SUCCESS ( finalStatus ) ) {
information = 0 ;
goto Exit ;
}
if ( generationCount1 = = generationCount2 ) {
//
// Block limits data stays the same during the call to ClasspDeviceGetLBAStatusWorker()
// The result from ClasspDeviceGetLBAStatusWorker() is valid.
//
finalStatus = getLBAWorkerStatus ;
if ( NT_SUCCESS ( finalStatus ) ) {
information = dsmOutputLength ;
}
break ;
}
//
// Try again with the latest block limits data
//
generationCount1 = generationCount2 ;
information = 0 ;
finalStatus = STATUS_DEVICE_DATA_ERROR ;
}
Exit :
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = finalStatus ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return finalStatus ;
}
NTSTATUS
ClasspDeviceGetLBAStatusWorker (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PCLASS_VPD_B0_DATA BlockLimitsData ,
_In_ ULONGLONG StartingOffset ,
_In_ ULONGLONG LengthInBytes ,
_Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput ,
_Inout_ PULONG DsmOutputLength ,
_Inout_ PSCSI_REQUEST_BLOCK Srb ,
_In_ BOOLEAN ConsolidateableBlocksOnly ,
_In_ ULONG OutputVersion ,
_Out_ PBOOLEAN BlockLimitsDataMayHaveChanged
)
/*
Routine Description :
This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation .
1. This function will only handle the first dataset range .
2. This function will not handle dataset ranges whose LengthInBytes is greater than :
( ( MAXULONG - sizeof ( LBA_STATUS_LIST_HEADER ) ) / sizeof ( LBA_STATUS_DESCRIPTOR ) ) * BytesPerSlab
The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
of slabs for which mapping status is desired .
The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
contains a bitmap that represents the mapped status of the slabs in the requested
range . Note that the number of slabs returned may be less than the number
requested .
Thus function will automatically re - align the given range offset if it was
not slab - aligned . The delta between the given range offset and the properly
aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE .
Arguments :
DeviceObject : The FDO of the device to which Get LBA Status will be sent .
BlockLimitsData : Block limits data of the device
StartingOffset : Starting byte offset of byte range to query LBA status ( must be sector aligned )
LengthInBytes : Length of byte range to query LBA status ( multiple of sector size )
DsmOutput : Output data buffer
DsmOutputLength : output data buffer size . It will be updated with actual bytes used .
Srb : This should be allocated and initialized before it ' s passed in . It
will be used for the Get LBA Status command .
ConsolidateableBlocksOnly : Only blocks that are eligible for consolidation
should be returned .
OutputVersion : The version of the DEVICE_DATA_SET_LB_PROVISIONING_STATE
structure to return . This should be one of :
- DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1
- DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2
BlockLimitsDataMayHaveChanged : if this function fails , this flag indicates
if the failure can be caused by changes in device ' s block limit data .
Return Value :
STATUS_INVALID_PARAMETER : May be returned under the following conditions :
- If the requested range was too large . The caller should try again with a
smaller range . See above for how to calculate the maximum range .
- If the given starting offset was not within the valid range of the device .
STATUS_NOT_SUPPORTED : The storage did not report some information critical to
the execution of this function ( e . g . Optimal Unmap Granularity ) .
STATUS_BUFFER_TOO_SMALL : The output buffer is not large enough to hold the max
data that could be returned from this function . If the output buffer is
at least the size of a ULONG , we will write the required output buffer size
to the first ULONG bytes of the output buffer .
STATUS_DEVICE_DATA_ERROR : The Get LBA Status command succeeded but did not
return data as expected .
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PDEVICE_DATA_SET_LB_PROVISIONING_STATE lbpState ;
ULONG bitMapGranularityInBits = FIELD_SIZE ( DEVICE_DATA_SET_LB_PROVISIONING_STATE , SlabAllocationBitMap [ 0 ] ) * 8 ;
ULONG requiredOutputLength ;
ULONG outputLength = * DsmOutputLength ;
ULONG blocksPerSlab ;
ULONGLONG bytesPerSlab ;
ULONGLONG alignmentInBytes = 0 ;
ULONG alignmentInBlocks = 0 ;
ULONG maxBufferSize ;
ULONG maxSlabs ;
ULONGLONG requestedSlabs ; // Total number of slabs requested by the caller.
ULONGLONG startingLBA ;
ULONGLONG startingOffsetDelta ;
ULONG totalProcessedSlabs = 0 ; // Total number of slabs we processed.
ULONGLONG slabsPerCommand ; // Number of slabs we can ask for in one Get LBA Status command.
BOOLEAN doneProcessing = FALSE ; // Indicates we should break out of the Get LBA Status loop.
ULONG lbaStatusSize ;
PLBA_STATUS_LIST_HEADER lbaStatusListHeader = NULL ;
//
// This function can fail if the block limits data on the device changes.
// This flag tells the caller if it should retry with a newer block limits data
//
* BlockLimitsDataMayHaveChanged = FALSE ;
//
// Make sure we're running at PASSIVE_LEVEL
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL )
{
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
status = STATUS_INVALID_LEVEL ;
goto Exit ;
}
//
// Don't send down a Get LBA Status command if UNMAP isn't supported.
//
if ( ! fdoExtension - > FunctionSupportInfo - > LBProvisioningData . LBPU )
{
return STATUS_NOT_SUPPORTED ;
goto Exit ;
}
//
// Make sure we have a non-zero value for the number of bytes per block.
// Otherwise we will end up dividing by zero later on.
//
if ( fdoExtension - > DiskGeometry . BytesPerSector = = 0 )
{
status = ClassReadDriveCapacity ( fdoExtension - > DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) | | fdoExtension - > DiskGeometry . BytesPerSector = = 0 )
{
status = STATUS_INVALID_DEVICE_REQUEST ;
goto Exit ;
}
}
//
// We only service the first dataset range specified.
//
if ( BlockLimitsData - > UGAVALID = = TRUE ) {
alignmentInBlocks = BlockLimitsData - > UnmapGranularityAlignment ;
alignmentInBytes = ( ULONGLONG ) alignmentInBlocks * ( ULONGLONG ) fdoExtension - > DiskGeometry . BytesPerSector ;
}
//
// Make sure the specified range is valid. The Unmap Granularity Alignment
// defines a region at the beginning of the disk that cannot be
// mapped/unmapped so the specified range should not include any part of that
// region.
//
if ( LengthInBytes = = 0 | |
StartingOffset < alignmentInBytes | |
StartingOffset + LengthInBytes > ( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Invalid range, length is %I64u bytes, starting offset is %I64u bytes, Unmap alignment is %I64u bytes, and disk size is %I64u bytes \n " ,
DeviceObject ,
LengthInBytes ,
StartingOffset ,
alignmentInBytes ,
( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
//
// Calculate the number of bytes per slab so that we can convert (and
// possibly align) the given offset (given in bytes) to slabs.
//
blocksPerSlab = BlockLimitsData - > OptimalUnmapGranularity ;
bytesPerSlab = ( ULONGLONG ) blocksPerSlab * ( ULONGLONG ) fdoExtension - > DiskGeometry . BytesPerSector ;
//
// If the starting offset is not slab-aligned, we need to adjust it to
// be aligned with the next highest slab. We also need to save the delta
// to return to the user later.
//
if ( ( ( StartingOffset - alignmentInBytes ) % bytesPerSlab ) ! = 0 )
{
startingLBA = ( ( ( StartingOffset - alignmentInBytes ) / bytesPerSlab ) + 1 ) * ( ULONGLONG ) blocksPerSlab + alignmentInBlocks ;
startingOffsetDelta = ( startingLBA * fdoExtension - > DiskGeometry . BytesPerSector ) - StartingOffset ;
}
else
{
startingLBA = ( ( StartingOffset - alignmentInBytes ) / bytesPerSlab ) * ( ULONGLONG ) blocksPerSlab + alignmentInBlocks ;
startingOffsetDelta = 0 ;
}
//
// Caclulate the number of slabs the caller requested.
//
if ( ( LengthInBytes % bytesPerSlab ) = = 0 ) {
requestedSlabs = ( LengthInBytes / bytesPerSlab ) ;
} else {
//
// Round up the number of requested slabs if the length indicates a
// partial slab. This should cover the case where the user specifies
// a dataset range for the whole disk, but the size of the disk is not
// a slab-multiple. Rounding up allows us to return the status of the
// partial slab
//
requestedSlabs = ( LengthInBytes / bytesPerSlab ) + 1 ;
}
//
// If the caller asked for no slabs then return STATUS_INVALID_PARAMETER.
//
if ( requestedSlabs = = 0 )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Invalid number (%I64u) of slabs requested \n " ,
DeviceObject ,
requestedSlabs ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto Exit ;
}
//
// Cap requested slabs at MAXULONG since SlabAllocationBitMapBitCount
// is a 4-byte field. We may return less data than requested, but the
// caller can simply re-query for the omitted portion(s).
//
requestedSlabs = min ( requestedSlabs , MAXULONG ) ;
//
// Calculate the required size of the output buffer based upon the desired
// version of the output structure.
// In the worst case, Get LBA Status returns a descriptor for each slab
// requested, thus the required output buffer length is equal to:
// 1. The size of DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT; plus
// 2. The size of DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2); plus
// 3. The size of a ULONG array large enough to hold a bit for each slab requested.
// (The first element is already allocated in DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2).)
//
# if (NTDDI_VERSION >= NTDDI_WINBLUE)
if ( OutputVersion = = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2 ) {
requiredOutputLength = ( ULONG ) ( sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT )
+ sizeof ( DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 )
+ ( ( ( requestedSlabs - 1 ) / bitMapGranularityInBits ) )
* FIELD_SIZE ( DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 , SlabAllocationBitMap [ 0 ] ) ) ;
} else
# else
UNREFERENCED_PARAMETER ( OutputVersion ) ;
# endif
{
requiredOutputLength = ( ULONG ) ( sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT )
+ sizeof ( DEVICE_DATA_SET_LB_PROVISIONING_STATE )
+ ( ( ( requestedSlabs - 1 ) / bitMapGranularityInBits ) )
* FIELD_SIZE ( DEVICE_DATA_SET_LB_PROVISIONING_STATE , SlabAllocationBitMap [ 0 ] ) ) ;
}
//
// The output buffer is not big enough to hold the requested data.
// Inform the caller of the correct buffer size.
//
if ( outputLength < requiredOutputLength )
{
status = STATUS_BUFFER_TOO_SMALL ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Given output buffer is %u bytes, needs to be %u bytes \n " ,
DeviceObject ,
outputLength ,
requiredOutputLength ) ) ;
//
// If the output buffer is big enough, write the required buffer
// length to the first ULONG bytes of the output buffer.
//
if ( outputLength > = sizeof ( ULONG ) )
{
* ( ( PULONG ) DsmOutput ) = requiredOutputLength ;
}
goto Exit ;
}
//
// Calculate the maximum number of slabs that could be returned by a single
// Get LBA Status command. The max buffer size could either be capped by
// the Parameter Data Length field or the Max Transfer Length of the
// adapter.
// The number of slabs we actually ask for in a single command is the
// smaller of the number of slabs requested by the user or the max number
// of slabs we can theoretically ask for in a single command.
//
maxBufferSize = MIN ( MAXULONG , fdoExtension - > PrivateFdoData - > HwMaxXferLen ) ;
maxSlabs = ( maxBufferSize - sizeof ( LBA_STATUS_LIST_HEADER ) ) / sizeof ( LBA_STATUS_DESCRIPTOR ) ;
slabsPerCommand = min ( requestedSlabs , maxSlabs ) ;
//
// Allocate the buffer that will contain the returned LBA Status Descriptors.
// Assume that in the worst case every other slab has a different mapping
// status. That means that there may be a descriptor for every slab requested.
//
lbaStatusSize = ( ULONG ) ( sizeof ( LBA_STATUS_LIST_HEADER ) + ( slabsPerCommand * sizeof ( LBA_STATUS_DESCRIPTOR ) ) ) ;
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
lbaStatusSize = ALIGN_UP_BY ( lbaStatusSize , KeGetRecommendedSharedDataAlignment ( ) ) ;
lbaStatusListHeader = ( PLBA_STATUS_LIST_HEADER ) ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , lbaStatusSize , CLASS_TAG_LB_PROVISIONING ) ;
# else
lbaStatusListHeader = ( PLBA_STATUS_LIST_HEADER ) ExAllocatePoolWithTag ( NonPagedPoolNx , lbaStatusSize , CLASS_TAG_LB_PROVISIONING ) ;
# endif
if ( lbaStatusListHeader = = NULL )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Failed to allocate %u bytes for descriptors \n " ,
DeviceObject ,
lbaStatusSize ) ) ;
NT_ASSERT ( lbaStatusListHeader ! = NULL ) ;
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit ;
}
//
// Set default values for the output buffer.
// If we process at least one slab from the device we will update the
// offset and lengths accordingly.
//
DsmOutput - > Action = DeviceDsmAction_Allocation ;
DsmOutput - > Size = sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT ) ;
DsmOutput - > OutputBlockOffset = 0 ;
DsmOutput - > OutputBlockLength = 0 ;
* DsmOutputLength = DsmOutput - > Size ;
//
// The returned DEVICE_DATA_SET_LB_PROVISIONING_STATE is at the end of the
// DSM output structure. Zero it out before we start to fill it in.
//
lbpState = Add2Ptr ( DsmOutput , sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT ) ) ;
RtlZeroMemory ( lbpState , requiredOutputLength - sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT ) ) ;
do {
//
// Send down GetLBAStatus for the current range.
//
status = GetLBAStatus ( fdoExtension ,
Srb ,
startingLBA ,
lbaStatusListHeader ,
lbaStatusSize ,
ConsolidateableBlocksOnly ) ;
if ( NT_SUCCESS ( status ) )
{
ULONG descrIndex = 0 ;
ULONG descrSize = 0 ;
ULONG descrSizeOverhead ;
ULONG descrCount = 0 ;
ULONGLONG expectedStartingLBA ;
BOOLEAN processCurrentDescriptor = TRUE ;
ULONG commandProcessedSlabs = 0 ; // Number of slabs processed for this command.
descrSizeOverhead = FIELD_OFFSET ( LBA_STATUS_LIST_HEADER , Descriptors [ 0 ] ) -
RTL_SIZEOF_THROUGH_FIELD ( LBA_STATUS_LIST_HEADER , ParameterLength ) ;
REVERSE_BYTES ( & descrSize , & ( lbaStatusListHeader - > ParameterLength ) ) ;
//
// If the returned Parameter Data Length field describes more
// descriptors than we allocated space for then make sure we don't
// try to process more descriptors than are actually present.
//
if ( descrSize > ( lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD ( LBA_STATUS_LIST_HEADER , ParameterLength ) ) ) {
descrSize = ( lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD ( LBA_STATUS_LIST_HEADER , ParameterLength ) ) ;
}
if ( descrSize > = descrSizeOverhead ) {
descrSize - = descrSizeOverhead ;
descrCount = descrSize / sizeof ( LBA_STATUS_DESCRIPTOR ) ;
//
// Make sure at least one descriptor was returned.
//
if ( descrCount > 0 ) {
//
// We expect the first starting LBA returned by the device to be the
// same starting LBA we specified in the command.
//
expectedStartingLBA = startingLBA ;
//
// Translate the returned LBA status descriptors into a bitmap where each bit represents
// a slab. The slab size is represented by the Optimal Unmap Granularity.
// 1 = The slab is mapped.
// 0 = The slab is unmapped (deallocated or anchored).
//
for ( descrIndex = 0 ; descrIndex < descrCount & & totalProcessedSlabs < requestedSlabs & & ! doneProcessing ; descrIndex + + )
{
PLBA_STATUS_DESCRIPTOR lbaStatusDescr = & ( lbaStatusListHeader - > Descriptors [ descrIndex ] ) ;
ULONGLONG returnedStartingLBA ;
ULONG mapped = ( lbaStatusDescr - > ProvisioningStatus ! = LBA_STATUS_MAPPED ) ? 0x0 : 0x1 ;
ULONG lbaCount = 0 ;
REVERSE_BYTES_QUAD ( & returnedStartingLBA , & ( lbaStatusDescr - > StartingLBA ) ) ;
REVERSE_BYTES ( & lbaCount , & ( lbaStatusDescr - > LogicalBlockCount ) ) ;
if ( returnedStartingLBA ! = expectedStartingLBA )
{
//
// We expect the descriptors will express a contiguous range of LBAs.
// If the starting LBA is not contiguous with the LBA range from the
// previous descriptor then we should not process any more descriptors,
// including the current one.
//
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Device returned starting LBA = %I64x when %I64x was expected. \n " ,
DeviceObject ,
returnedStartingLBA ,
startingLBA ) ) ;
doneProcessing = TRUE ;
processCurrentDescriptor = FALSE ;
* BlockLimitsDataMayHaveChanged = TRUE ;
}
else if ( lbaCount > 0 & & lbaCount % blocksPerSlab ! = 0 )
{
//
// If the device returned an LBA count with a partial slab, round
// the LBA count up to the nearest slab and set a flag to stop
// processing further descriptors. This is mainly to handle the
// case where disk size may not be slab-aligned and thus the last
// "slab" is actually a partial slab.
//
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Device returned an LBA count (%u) that is not a multiple of the slab size (%u) \n " ,
DeviceObject ,
lbaCount ,
blocksPerSlab ) ) ;
lbaCount = ( ( lbaCount / blocksPerSlab ) + 1 ) * blocksPerSlab ;
doneProcessing = TRUE ;
processCurrentDescriptor = TRUE ;
}
else if ( lbaCount = = 0 )
{
//
// If the LBA count is 0, just skip this descriptor.
//
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Device returned a zero LBA count \n " ,
DeviceObject ) ) ;
processCurrentDescriptor = FALSE ;
}
//
// Generate bits for the slabs described in the current descriptor.
// It's possible the device may have returned more slabs than requested
// so we make sure to stop once we've processed all we need.
//
if ( processCurrentDescriptor )
{
ULONG descrSlabs = lbaCount / blocksPerSlab ; // Number of slabs in this descriptor.
for ( ; 0 < descrSlabs & & totalProcessedSlabs < requestedSlabs ; descrSlabs - - , commandProcessedSlabs + + , totalProcessedSlabs + + )
{
ULONG bitMapIndex = totalProcessedSlabs / bitMapGranularityInBits ;
ULONG bitPos = totalProcessedSlabs % bitMapGranularityInBits ;
# if (NTDDI_VERSION >= NTDDI_WINBLUE)
if ( OutputVersion = = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2 ) {
( ( PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 ) lbpState ) - > SlabAllocationBitMap [ bitMapIndex ] | = ( mapped < < bitPos ) ;
} else
# endif
{
lbpState - > SlabAllocationBitMap [ bitMapIndex ] | = ( mapped < < bitPos ) ;
}
}
}
//
// Calculate the next expected starting LBA.
//
expectedStartingLBA = returnedStartingLBA + lbaCount ;
}
if ( commandProcessedSlabs > 0 ) {
//
// Calculate the starting LBA we'll use for the next command.
//
startingLBA + = ( ( ULONGLONG ) commandProcessedSlabs * ( ULONGLONG ) blocksPerSlab ) ;
} else {
//
// This should never happen, but we should handle it gracefully anyway.
//
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): The slab allocation bitmap has zero length. \n " ,
DeviceObject ) ) ;
NT_ASSERT ( commandProcessedSlabs ! = 0 ) ;
doneProcessing = TRUE ;
status = STATUS_UNSUCCESSFUL ;
}
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Device returned no LBA Status Descriptors. \n " ,
DeviceObject ) ) ;
doneProcessing = TRUE ;
status = STATUS_UNSUCCESSFUL ;
}
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): not enough bytes returned \n " ,
DeviceObject ) ) ;
doneProcessing = TRUE ;
status = STATUS_DEVICE_DATA_ERROR ;
}
}
//
// Loop until we encounter some error or we've processed all the requested slabs.
//
} while ( NT_SUCCESS ( status ) & &
! doneProcessing & &
( totalProcessedSlabs < requestedSlabs ) ) ;
//
// At least one slab was returned by the device and processed, which we
// consider success. It's up to the caller to detect truncation.
// Update the output buffer sizes, offsets, etc. accordingly.
//
if ( totalProcessedSlabs > 0 ) {
# if (NTDDI_VERSION >= NTDDI_WINBLUE)
if ( OutputVersion = = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2 ) {
PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 lbpStateV2 = ( PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 ) lbpState ;
lbpStateV2 - > SlabSizeInBytes = bytesPerSlab ;
lbpStateV2 - > SlabOffsetDeltaInBytes = startingOffsetDelta ;
lbpStateV2 - > SlabAllocationBitMapBitCount = totalProcessedSlabs ;
lbpStateV2 - > SlabAllocationBitMapLength = ( ( totalProcessedSlabs - 1 ) / ( ULONGLONG ) bitMapGranularityInBits ) + 1 ;
lbpStateV2 - > Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2 ;
//
// Note that there is already one element of the bitmap array allocated
// in the DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 structure itself, which
// is why we subtract 1 from SlabAllocationBitMapLength.
//
lbpStateV2 - > Size = sizeof ( DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 )
+ ( ( lbpStateV2 - > SlabAllocationBitMapLength - 1 ) * sizeof ( lbpStateV2 - > SlabAllocationBitMap [ 0 ] ) ) ;
} else
# endif
{
lbpState - > SlabSizeInBytes = bytesPerSlab ;
lbpState - > SlabOffsetDeltaInBytes = ( ULONG ) startingOffsetDelta ;
lbpState - > SlabAllocationBitMapBitCount = totalProcessedSlabs ;
lbpState - > SlabAllocationBitMapLength = ( ( totalProcessedSlabs - 1 ) / bitMapGranularityInBits ) + 1 ;
lbpState - > Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1 ;
//
// Note that there is already one element of the bitmap array allocated
// in the DEVICE_DATA_SET_LB_PROVISIONING_STATE structure itself, which
// is why we subtract 1 from SlabAllocationBitMapLength.
//
lbpState - > Size = sizeof ( DEVICE_DATA_SET_LB_PROVISIONING_STATE )
+ ( ( lbpState - > SlabAllocationBitMapLength - 1 ) * sizeof ( lbpState - > SlabAllocationBitMap [ 0 ] ) ) ;
}
DsmOutput - > OutputBlockLength = lbpState - > Size ; // Size is at the same offset in all versions of the structure.
DsmOutput - > OutputBlockOffset = sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT ) ;
* DsmOutputLength = DsmOutput - > Size + DsmOutput - > OutputBlockLength ;
status = STATUS_SUCCESS ;
}
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceGetLBAStatusWorker (%p): Processed a total of %u slabs \n " ,
DeviceObject ,
totalProcessedSlabs ) ) ;
Exit :
FREE_POOL ( lbaStatusListHeader ) ;
return status ;
}
NTSTATUS ClassGetLBProvisioningLogPage (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PSCSI_REQUEST_BLOCK Srb ,
_In_ ULONG LogPageSize ,
_Inout_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage
)
/*
Routine Description :
This function sends a LOG SENSE command to the given device and returns the
Logical Block Provisioning Log Page , if available .
Arguments :
DeviceObject : The FDO of the device to which the Log Sense command will be sent .
Srb : This should be allocated before it is passed in , but it does not have
to be initialized . This function will initialize it .
LogPageSize : The size of the LogPage buffer in bytes .
LogPage : A pointer to an already allocated output buffer that may contain
the LBP log page when this function returns .
Return Value :
STATUS_INVALID_PARAMETER : May be returned if the LogPage buffer is NULL or
not large enough .
STATUS_SUCCESS : The log page was obtained and placed in the LogPage buffer .
This function may return other NTSTATUS codes from internal function calls .
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
NTSTATUS status = STATUS_SUCCESS ;
PCDB cdb = NULL ;
//
// Make sure the caller passed in an adequate output buffer. The Allocation
// Length field in the Log Sense command is only 2 bytes so we need to also
// make sure that the given log page size isn't larger than MAXUSHORT.
//
if ( LogPage = = NULL | |
LogPageSize < sizeof ( LOG_PAGE_LOGICAL_BLOCK_PROVISIONING ) | |
LogPageSize > MAXUSHORT )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassGetLBProvisioningLogPage: DO (%p), Invalid parameter, LogPage = %p, LogPageSize = %u. \n " ,
DeviceObject ,
LogPage ,
LogPageSize ) ) ;
return STATUS_INVALID_PARAMETER ;
}
//
// Initialize the SRB.
//
if ( fdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) {
status = InitializeStorageRequestBlock ( ( PSTORAGE_REQUEST_BLOCK ) Srb ,
STORAGE_ADDRESS_TYPE_BTL8 ,
CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE ,
1 ,
SrbExDataTypeScsiCdb16 ) ;
if ( NT_SUCCESS ( status ) ) {
( ( PSTORAGE_REQUEST_BLOCK ) Srb ) - > SrbFunction = SRB_FUNCTION_EXECUTE_SCSI ;
} else {
//
// Should not occur.
//
NT_ASSERT ( FALSE ) ;
}
} else {
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = SRB_FUNCTION_EXECUTE_SCSI ;
}
//
// Build and send down the Log Sense command.
//
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue ) ;
SrbSetRequestTag ( Srb , SP_UNTAGGED ) ;
SrbSetRequestAttribute ( Srb , SRB_SIMPLE_TAG_REQUEST ) ;
SrbAssignSrbFlags ( Srb , fdoExtension - > SrbFlags ) ;
SrbSetCdbLength ( Srb , sizeof ( cdb - > LOGSENSE ) ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > LOGSENSE . OperationCode = SCSIOP_LOG_SENSE ;
cdb - > LOGSENSE . PageCode = LOG_PAGE_CODE_LOGICAL_BLOCK_PROVISIONING ;
cdb - > LOGSENSE . PCBit = 0 ;
cdb - > LOGSENSE . ParameterPointer [ 0 ] = 0 ;
cdb - > LOGSENSE . ParameterPointer [ 1 ] = 0 ;
REVERSE_BYTES_SHORT ( & ( cdb - > LOGSENSE . AllocationLength ) , & LogPageSize ) ;
status = ClassSendSrbSynchronous ( fdoExtension - > DeviceObject ,
Srb ,
LogPage ,
LogPageSize ,
FALSE ) ;
//
// Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
// buffer was larger than necessary.
//
if ( status = = STATUS_DATA_OVERRUN & &
SrbGetDataTransferLength ( Srb ) < LogPageSize )
{
status = STATUS_SUCCESS ;
}
//
// Log the command.
//
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" ClassGetLBProvisioningLogPage: DO (%p), LogSense command issued for LBP log page. NT Status: %!STATUS!. \n " ,
DeviceObject ,
status
) ) ;
return status ;
}
NTSTATUS ClassInterpretLBProvisioningLogPage (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ ULONG LogPageSize ,
_In_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage ,
_In_ ULONG ResourcesSize ,
_Out_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
)
/*
Routine Description :
This function takes a Logical Block Provisioning log page ( returned by
ClassGetLBProvisioningLogPage ( ) , for example ) , interprets its contents ,
and returns the interpreted data in a STORAGE_LB_PROVISIONING_MAP_RESOURCES
structure .
None , some , or all of the data in the output buffer may be valid . The
caller must look at the individual " Valid " fields to see which fields have
valid data .
Arguments :
DeviceObject : The FDO of the device from which the log page was obtained .
LogPageSize : The size of the LogPage buffer in bytes .
LogPage : A pointer to a valid LBP log page structure .
ResourcesSize : The size of the Resources buffer in bytes .
Resources : A pointer to an already allocated output buffer that may contain
the interpreted log page data when this function returns .
Return Value :
STATUS_NOT_SUPPORTED : May be returned if the threshold exponent from the
0xB2 page is invalid .
STATUS_INVALID_PARAMETER : May be returned if either the LogPage or Resources
buffers are NULL or too small .
STATUS_SUCCESS : The log page data was interpreted and the Resources output
buffer has data in it .
This function may return other NTSTATUS codes from internal function calls .
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
USHORT pageLength ;
PLOG_PARAMETER_HEADER parameter ;
PVOID endOfPage ;
USHORT parameterCode ;
ULONG resourceCount ;
UCHAR thresholdExponent = fdoExtension - > FunctionSupportInfo - > LBProvisioningData . ThresholdExponent ;
ULONGLONG thresholdSetSize ;
//
// SBC-3 states that the threshold exponent (from the 0xB2 VPD page), must
// be non-zero and less than or equal to 32.
//
if ( thresholdExponent < 0 | | thresholdExponent > 32 )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassInterpretLBProvisioningLogPage: DO (%p), Threshold Exponent (%u) is invalid. \n " ,
DeviceObject ,
thresholdExponent ) ) ;
return STATUS_NOT_SUPPORTED ;
}
if ( Resources = = NULL | |
ResourcesSize < sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) | |
LogPage = = NULL | |
LogPageSize < sizeof ( LOG_PAGE_LOGICAL_BLOCK_PROVISIONING ) )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassInterpretLBProvisioningLogPage: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u, LogPage = %p, LogPageSize = %u. \n " ,
DeviceObject ,
Resources ,
ResourcesSize ,
LogPage ,
LogPageSize ) ) ;
return STATUS_INVALID_PARAMETER ;
}
//
// Calculate the threshold set size (in LBAs).
//
thresholdSetSize = 1ULL < < thresholdExponent ;
REVERSE_BYTES_SHORT ( & pageLength , & ( LogPage - > PageLength ) ) ;
//
// Initialize the output buffer.
//
RtlZeroMemory ( Resources , sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) ) ;
Resources - > Size = sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) ;
Resources - > Version = sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) ;
//
// Make sure we don't walk off the end of the log page buffer
// if pageLength is somehow longer than the buffer itself.
//
pageLength = ( USHORT ) min ( pageLength , ( LogPageSize - FIELD_OFFSET ( LOG_PAGE_LOGICAL_BLOCK_PROVISIONING , Parameters ) ) ) ;
parameter = ( PLOG_PARAMETER_HEADER ) ( ( PUCHAR ) LogPage + FIELD_OFFSET ( LOG_PAGE_LOGICAL_BLOCK_PROVISIONING , Parameters ) ) ;
endOfPage = ( PVOID ) ( ( PUCHAR ) parameter + pageLength ) ;
//
// Walk the parameters.
//
while ( ( PVOID ) parameter < endOfPage )
{
if ( parameter - > ParameterLength > 0 )
{
REVERSE_BYTES_SHORT ( & parameterCode , & ( parameter - > ParameterCode ) ) ;
switch ( parameterCode )
{
case LOG_PAGE_LBP_PARAMETER_CODE_AVAILABLE :
{
REVERSE_BYTES ( & resourceCount , & ( ( ( PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT ) parameter ) - > ResourceCount ) ) ;
Resources - > AvailableMappingResources = ( ULONGLONG ) resourceCount * thresholdSetSize * ( ULONGLONG ) fdoExtension - > DiskGeometry . BytesPerSector ;
Resources - > AvailableMappingResourcesValid = TRUE ;
//
// Devices that implement SBC-3 revisions older than r27 will not specify
// an LBP log page parameter that has fields beyond ResourceCount.
//
if ( parameter - > ParameterLength > FIELD_OFFSET ( LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT , ResourceCount [ 3 ] ) ) {
Resources - > AvailableMappingResourcesScope = ( ( PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT ) parameter ) - > Scope ;
}
break ;
}
case LOG_PAGE_LBP_PARAMETER_CODE_USED :
{
REVERSE_BYTES ( & resourceCount , & ( ( ( PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT ) parameter ) - > ResourceCount ) ) ;
Resources - > UsedMappingResources = ( ULONGLONG ) resourceCount * thresholdSetSize * ( ULONGLONG ) fdoExtension - > DiskGeometry . BytesPerSector ;
Resources - > UsedMappingResourcesValid = TRUE ;
//
// Devices that implement SBC-3 revisions older than r27 will not specify
// an LBP log page parameter that has fields beyond ResourceCount.
//
if ( parameter - > ParameterLength > FIELD_OFFSET ( LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT , ResourceCount [ 3 ] ) ) {
Resources - > UsedMappingResourcesScope = ( ( PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT ) parameter ) - > Scope ;
}
break ;
}
}
}
//
// Move to the next parameter.
//
parameter = ( PLOG_PARAMETER_HEADER ) ( ( PUCHAR ) parameter + sizeof ( LOG_PARAMETER_HEADER ) + parameter - > ParameterLength ) ;
}
return STATUS_SUCCESS ;
}
NTSTATUS ClassGetLBProvisioningResources (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PSCSI_REQUEST_BLOCK Srb ,
_In_ ULONG ResourcesSize ,
_Inout_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
)
/*
Routine Description :
This function obtains the Logical Block Provisioning log page , interprets
its contents , and returns the interpreted data in a
STORAGE_LB_PROVISIONING_MAP_RESOURCES structure .
None , some , or all of the data in the output buffer may be valid . The
caller must look at the individual " Valid " fields to see which fields have
valid data .
Arguments :
DeviceObject : The target FDO .
Srb : This should be allocated before it is passed in , but it does not have
to be initialized .
ResourcesSize : The size of the Resources buffer in bytes .
Resources : A pointer to an already allocated output buffer that may contain
the interpreted log page data when this function returns .
Return Value :
STATUS_NOT_SUPPORTED : May be returned if the device does not have LBP enabled .
STATUS_INVALID_PARAMETER : May be returned if either the Resources buffer is
NULL or too small .
STATUS_INSUFFICIENT_RESOURCES : May be returned if a log page buffer could not
be allocated .
STATUS_SUCCESS : The log page data was obtained and the Resources output
buffer has data in it .
This function may return other NTSTATUS codes from internal function calls .
- - */
{
NTSTATUS status ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
ULONG logPageSize ;
PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING logPage = NULL ;
//
// This functionality is only supported for devices that support logical
// block provisioning.
//
if ( fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . LBProvisioning = = FALSE )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassGetLBProvisioningResources: DO (%p), Device does not support logical block provisioning. \n " ,
DeviceObject ) ) ;
return STATUS_NOT_SUPPORTED ;
}
//
// Validate the output buffer.
//
if ( Resources = = NULL | |
ResourcesSize < sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) )
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassGetLBProvisioningResources: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u. \n " ,
DeviceObject ,
Resources ,
ResourcesSize ) ) ;
return STATUS_INVALID_PARAMETER ;
}
//
// Allocate a buffer for the log page. Currently the log page contains:
// 1. Log page header
// 2. Log page parameter for used resources
// 3. Log page parameter for available resources
//
logPageSize = sizeof ( LOG_PAGE_LOGICAL_BLOCK_PROVISIONING ) + ( 2 * sizeof ( LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT ) ) ;
# if defined(_ARM_) || defined(_ARM64_)
//
// ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
// based platforms. We are taking the conservative approach here.
//
logPageSize = ALIGN_UP_BY ( logPageSize , KeGetRecommendedSharedDataAlignment ( ) ) ;
logPage = ( PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING ) ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned , logPageSize , CLASS_TAG_LB_PROVISIONING ) ;
# else
logPage = ( PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING ) ExAllocatePoolWithTag ( NonPagedPoolNx , logPageSize , CLASS_TAG_LB_PROVISIONING ) ;
# endif
if ( logPage ! = NULL )
{
//
// Get the LBP log page from the device.
//
status = ClassGetLBProvisioningLogPage ( DeviceObject ,
Srb ,
logPageSize ,
logPage ) ;
if ( NT_SUCCESS ( status ) )
{
//
// Interpret the log page and fill in the output buffer.
//
status = ClassInterpretLBProvisioningLogPage ( DeviceObject ,
logPageSize ,
logPage ,
ResourcesSize ,
Resources ) ;
}
ExFreePool ( logPage ) ;
}
else
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassGetLBProvisioningResources: DO (%p), Failed to allocate memory for LBP log page. \n " ,
DeviceObject ) ) ;
status = STATUS_INSUFFICIENT_RESOURCES ;
}
return status ;
}
NTSTATUS
ClassDeviceGetLBProvisioningResources (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*
Routine Description :
This function returns the LBP resource counts in a
STORAGE_LB_PROVISIONING_MAP_RESOURCES structure in the IRP .
None , some , or all of the data in the output buffer may be valid . The
caller must look at the individual " Valid " fields to see which fields have
valid data .
Arguments :
DeviceObject : The target FDO .
Irp : The IRP which will contain the output buffer upon completion .
Srb : This should be allocated before it is passed in , but it does not have
to be initialized .
Return Value :
Some NTSTATUS code .
- - */
{
NTSTATUS status ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_LB_PROVISIONING_MAP_RESOURCES mapResources = ( PSTORAGE_LB_PROVISIONING_MAP_RESOURCES ) Irp - > AssociatedIrp . SystemBuffer ;
status = ClassGetLBProvisioningResources ( DeviceObject ,
Srb ,
irpStack - > Parameters . DeviceIoControl . OutputBufferLength ,
mapResources ) ;
if ( NT_SUCCESS ( status ) ) {
Irp - > IoStatus . Information = mapResources - > Size ;
} else {
Irp - > IoStatus . Information = 0 ;
}
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
_Function_class_ ( IO_WORKITEM_ROUTINE )
_IRQL_requires_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassLogThresholdEvent (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_opt_ PVOID Context
)
/*
Routine Description :
This function logs a logical block provisioning soft threshold event to the
system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the soft
threshold .
Context : A pointer to the IO_WORKITEM in which this function is running .
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
PIO_WORKITEM workItem = ( PIO_WORKITEM ) Context ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PSCSI_REQUEST_BLOCK srb = NULL ;
STORAGE_LB_PROVISIONING_MAP_RESOURCES resources = { 0 } ;
ULONG resourcesSize = sizeof ( STORAGE_LB_PROVISIONING_MAP_RESOURCES ) ;
PIO_ERROR_LOG_PACKET errorLogEntry = NULL ;
ULONG logEntrySize = sizeof ( IO_ERROR_LOG_PACKET ) ;
PWCHAR stringIndex = NULL ;
LONG stringSize = 0 ;
ULONG srbSize ;
//
// Allocate an SRB for getting the LBP log page.
//
if ( ( fdoExtension - > AdapterDescriptor ! = NULL ) & &
( fdoExtension - > AdapterDescriptor - > SrbType = = SRB_TYPE_STORAGE_REQUEST_BLOCK ) ) {
srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE ;
} else {
srbSize = sizeof ( SCSI_REQUEST_BLOCK ) ;
}
srb = ExAllocatePoolWithTag ( NonPagedPoolNx ,
srbSize ,
' ACcS ' ) ;
if ( srb ! = NULL ) {
//
// Try to get the LBP resources from the device so we can report them in
// the system event log.
//
ClassGetLBProvisioningResources ( DeviceObject ,
srb ,
resourcesSize ,
& resources ) ;
//
// We need to allocate enough space for 3 insertion strings:
// The first is a ULONG representing the disk number in decimal, which means
// a max of 10 digits, plus one for the NULL character.
// The second and third are ULONGLONGs representing the used and available
// bytes, which means a max of 20 digits, plus one for the NULL character.
// Make sure we do not exceed the max error log size or the max size of a
// UCHAR since the size gets truncated to a UCHAR when we pass it to
// IoAllocateErrorLogEntry().
//
logEntrySize = sizeof ( IO_ERROR_LOG_PACKET ) + ( 11 * sizeof ( WCHAR ) ) + ( 2 * ( 21 * sizeof ( WCHAR ) ) ) ;
logEntrySize = min ( logEntrySize , ERROR_LOG_MAXIMUM_SIZE ) ;
logEntrySize = min ( logEntrySize , MAXUCHAR ) ;
errorLogEntry = ( PIO_ERROR_LOG_PACKET ) IoAllocateErrorLogEntry ( DeviceObject , ( UCHAR ) logEntrySize ) ;
if ( errorLogEntry ! = NULL )
{
//
// There are two event IDs we can use here. Both use the disk number,
// but one reports the available and used bytes while the other does not.
// We fall back on the latter if we failed to obtain the available and
// used byte counts from the LBP log page.
//
// The event insertion strings need to be in this order:
// 1. The disk number. (Both event IDs use this.)
// 2. Bytes used.
// 3. Bytes available.
//
RtlZeroMemory ( errorLogEntry , logEntrySize ) ;
errorLogEntry - > StringOffset = sizeof ( IO_ERROR_LOG_PACKET ) ;
stringIndex = ( PWCHAR ) ( ( ULONG_PTR ) errorLogEntry + sizeof ( IO_ERROR_LOG_PACKET ) ) ;
stringSize = logEntrySize - sizeof ( IO_ERROR_LOG_PACKET ) ;
//
// Add the disk number to the insertion strings.
//
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " %d " , fdoExtension - > DeviceNumber ) ;
if ( NT_SUCCESS ( status ) )
{
errorLogEntry - > NumberOfStrings + + ;
if ( resources . UsedMappingResourcesValid & &
resources . AvailableMappingResourcesValid )
{
//
// Add the used mapping resources to the insertion strings.
//
stringIndex + = ( wcslen ( stringIndex ) + 1 ) ;
stringSize - = ( LONG ) ( wcslen ( stringIndex ) + 1 ) * sizeof ( WCHAR ) ;
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " %I64u " , resources . UsedMappingResources ) ;
if ( NT_SUCCESS ( status ) )
{
errorLogEntry - > NumberOfStrings + + ;
//
// Add the available mapping resources to the insertion strings.
//
stringIndex + = ( wcslen ( stringIndex ) + 1 ) ;
stringSize - = ( LONG ) ( wcslen ( stringIndex ) + 1 ) * sizeof ( WCHAR ) ;
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " %I64u " , resources . AvailableMappingResources ) ;
if ( NT_SUCCESS ( status ) )
{
errorLogEntry - > NumberOfStrings + + ;
}
}
}
else
{
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_GENERAL ,
" ClassLogThresholdEvent: DO (%p), Used and available mapping resources were unavailable. \n " ,
DeviceObject ) ) ;
}
}
//
// If we were able to successfully assemble all 3 insertion strings,
// then we can use one of the "extended" event IDs. Otherwise, use the basic
// event ID, which only requires the disk number.
//
if ( errorLogEntry - > NumberOfStrings = = 3 )
{
if ( resources . UsedMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN & &
resources . AvailableMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN ) {
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_LUN ;
} else if ( resources . UsedMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN & &
resources . AvailableMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN ) {
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_POOL ;
} else if ( resources . UsedMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN & &
resources . AvailableMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN ) {
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_LUN ;
} else if ( resources . UsedMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN & &
resources . AvailableMappingResourcesScope = = LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN ) {
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_POOL ;
} else {
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX ;
}
}
else
{
errorLogEntry - > ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED ;
}
//
// Write the error log packet to the system error logging thread.
// It will be freed automatically.
//
IoWriteErrorLogEntry ( errorLogEntry ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassLogThresholdEvent: DO (%p), Soft threshold notification logged. \n " ,
DeviceObject ) ) ;
}
else
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogThresholdEvent: DO (%p), Failed to allocate memory for error log entry. \n " ,
DeviceObject ) ) ;
}
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogThresholdEvent: DO (%p), Failed to allocate memory for SRB. \n " ,
DeviceObject ) ) ;
}
//
// Clear the soft threshold event pending flag so that another can be queued.
//
InterlockedExchange ( ( PLONG ) & ( fdoExtension - > FunctionSupportInfo - > LBProvisioningData . SoftThresholdEventPending ) , 0 ) ;
ClassReleaseRemoveLock ( DeviceObject , ( PIRP ) workItem ) ;
FREE_POOL ( srb ) ;
if ( workItem ! = NULL ) {
IoFreeWorkItem ( workItem ) ;
}
}
NTSTATUS
ClasspLogSystemEventWithDeviceNumber (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ NTSTATUS IoErrorCode
)
/*
Routine Description :
This function is a helper routine to log any system events that require
the DeviceNumber ( e . g . disk number ) . It is basically a wrapper for the
IoWriteErrorLogEntry call .
Arguments :
DeviceObject : The FDO that represents the device for which the event needs to be logged .
IoErrorCode : The IO error code for the event .
Return Value :
STATUS_SUCCESS - if the event was logged
STATUS_INSUFFICIENT_RESOURCES - otherwise
- - */
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
PIO_ERROR_LOG_PACKET errorLogEntry = NULL ;
ULONG logEntrySize = sizeof ( IO_ERROR_LOG_PACKET ) ;
PWCHAR stringIndex = NULL ;
LONG stringSize = 0 ;
//
// We need to allocate enough space for one insertion string: a ULONG
// representing the disk number in decimal, which means a max of 10 digits,
// plus one for the NULL character.
// Make sure we do not exceed the max error log size or the max size of a
// UCHAR since the size gets truncated to a UCHAR when we pass it to
// IoAllocateErrorLogEntry().
//
logEntrySize = sizeof ( IO_ERROR_LOG_PACKET ) + ( 11 * sizeof ( WCHAR ) ) ;
logEntrySize = min ( logEntrySize , ERROR_LOG_MAXIMUM_SIZE ) ;
logEntrySize = min ( logEntrySize , MAXUCHAR ) ;
errorLogEntry = ( PIO_ERROR_LOG_PACKET ) IoAllocateErrorLogEntry ( DeviceObject , ( UCHAR ) logEntrySize ) ;
if ( errorLogEntry ) {
RtlZeroMemory ( errorLogEntry , logEntrySize ) ;
errorLogEntry - > StringOffset = sizeof ( IO_ERROR_LOG_PACKET ) ;
errorLogEntry - > ErrorCode = IoErrorCode ;
stringIndex = ( PWCHAR ) ( ( ULONG_PTR ) errorLogEntry + sizeof ( IO_ERROR_LOG_PACKET ) ) ;
stringSize = logEntrySize - sizeof ( IO_ERROR_LOG_PACKET ) ;
//
// Add the disk number to the insertion strings.
//
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " %d " , fdoExtension - > DeviceNumber ) ;
if ( NT_SUCCESS ( status ) ) {
errorLogEntry - > NumberOfStrings + + ;
}
//
// Write the error log packet to the system error logging thread.
// It will be freed automatically.
//
IoWriteErrorLogEntry ( errorLogEntry ) ;
status = STATUS_SUCCESS ;
}
return status ;
}
_Function_class_ ( IO_WORKITEM_ROUTINE )
_IRQL_requires_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassLogResourceExhaustionEvent (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_opt_ PVOID Context
)
/*
Routine Description :
This function logs a logical block provisioning permanent resource exhaustion
event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the permanent
resource exhaustion .
Context : A pointer to the IO_WORKITEM in which this function is running .
- - */
{
PIO_WORKITEM workItem = ( PIO_WORKITEM ) Context ;
if ( NT_SUCCESS ( ClasspLogSystemEventWithDeviceNumber ( DeviceObject , IO_ERROR_DISK_RESOURCES_EXHAUSTED ) ) ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassLogResourceExhaustionEvent: DO (%p), Permanent resource exhaustion logged. \n " ,
DeviceObject ) ) ;
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogResourceExhaustionEvent: DO (%p), Failed to allocate memory for error log entry. \n " ,
DeviceObject ) ) ;
}
ClassReleaseRemoveLock ( DeviceObject , ( PIRP ) workItem ) ;
if ( workItem ! = NULL ) {
IoFreeWorkItem ( workItem ) ;
}
}
VOID ClassQueueThresholdEventWorker (
_In_ PDEVICE_OBJECT DeviceObject
)
/*
Routine Description :
This function queues a delayed work item that will eventually log a
logical block provisioning soft threshold event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the soft
threshold .
- - */
{
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PIO_WORKITEM workItem = NULL ;
if ( commonExtension - > IsFdo & &
InterlockedCompareExchange ( ( PLONG ) & ( fdoExtension - > FunctionSupportInfo - > LBProvisioningData . SoftThresholdEventPending ) , 1 , 0 ) = = 0 )
{
workItem = IoAllocateWorkItem ( DeviceObject ) ;
if ( workItem )
{
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassQueueThresholdEventWorker: DO (%p), Queueing soft threshold notification work item. \n " ,
DeviceObject ) ) ;
ClassAcquireRemoveLock ( DeviceObject , ( PIRP ) ( workItem ) ) ;
//
// Queue a work item to write the threshold notification to the
// system event log.
//
IoQueueWorkItem ( workItem , ClassLogThresholdEvent , DelayedWorkQueue , workItem ) ;
}
else
{
//
// Clear the soft threshold event pending flag since this is normally
// done when the work item completes.
//
InterlockedExchange ( ( PLONG ) & ( fdoExtension - > FunctionSupportInfo - > LBProvisioningData . SoftThresholdEventPending ) , 0 ) ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassQueueThresholdEventWorker: DO (%p), Failed to allocate memory for the work item. \n " ,
DeviceObject ) ) ;
}
}
}
VOID ClassQueueResourceExhaustionEventWorker (
_In_ PDEVICE_OBJECT DeviceObject
)
/*
Routine Description :
This function queues a delayed work item that will eventually log a
logical block provisioning permanent resource exhaustion event to the
system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the resource
exhaustion .
- - */
{
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PIO_WORKITEM workItem = NULL ;
if ( commonExtension - > IsFdo )
{
workItem = IoAllocateWorkItem ( DeviceObject ) ;
if ( workItem )
{
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassQueueResourceExhaustionEventWorker: DO (%p), Queueing permanent resource exhaustion event work item. \n " ,
DeviceObject ) ) ;
ClassAcquireRemoveLock ( DeviceObject , ( PIRP ) ( workItem ) ) ;
//
// Queue a work item to write the threshold notification to the
// system event log.
//
IoQueueWorkItem ( workItem , ClassLogResourceExhaustionEvent , DelayedWorkQueue , workItem ) ;
}
else
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassQueueResourceExhaustionEventWorker: DO (%p), Failed to allocate memory for the work item. \n " ,
DeviceObject ) ) ;
}
}
}
_Function_class_ ( IO_WORKITEM_ROUTINE )
_IRQL_requires_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassLogCapacityChangedProcess (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_opt_ PVOID Context
)
/*
Routine Description :
This function logs a capacity changed event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the capacity change .
Context : A pointer to the IO_WORKITEM in which this function is running .
- - */
{
NTSTATUS status ;
PIO_WORKITEM workItem = ( PIO_WORKITEM ) Context ;
status = ClasspLogSystemEventWithDeviceNumber ( DeviceObject , IO_WARNING_DISK_CAPACITY_CHANGED ) ;
if ( NT_SUCCESS ( status ) ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassLogCapacityChangedEvent: DO (%p), Capacity changed logged. \n " ,
DeviceObject ) ) ;
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogCapacityChangedEvent: DO (%p), Failed to allocate memory for error log entry. \n " ,
DeviceObject ) ) ;
}
//
// Get disk capacity and notify upper layer if capacity is changed.
//
status = ClassReadDriveCapacity ( DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogCapacityChangedEvent: DO (%p), ClassReadDriveCapacity returned %!STATUS!. \n " ,
DeviceObject ,
status ) ) ;
}
ClassReleaseRemoveLock ( DeviceObject , ( PIRP ) workItem ) ;
if ( workItem ! = NULL ) {
IoFreeWorkItem ( workItem ) ;
}
}
VOID
ClassQueueCapacityChangedEventWorker (
_In_ PDEVICE_OBJECT DeviceObject
)
/*
Routine Description :
This function queues a delayed work item that will eventually log a
disk capacity changed event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the capacity change .
- - */
{
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PIO_WORKITEM workItem = NULL ;
if ( commonExtension - > IsFdo )
{
workItem = IoAllocateWorkItem ( DeviceObject ) ;
if ( workItem )
{
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassQueueCapacityChangedEventWorker: DO (%p), Queueing capacity changed event work item. \n " ,
DeviceObject ) ) ;
ClassAcquireRemoveLock ( DeviceObject , ( PIRP ) ( workItem ) ) ;
//
// Queue a work item to write the threshold notification to the
// system event log.
//
IoQueueWorkItem ( workItem , ClassLogCapacityChangedProcess , DelayedWorkQueue , workItem ) ;
}
else
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassQueueCapacityChangedEventWorker: DO (%p), Failed to allocate memory for the work item. \n " ,
DeviceObject ) ) ;
}
}
}
_Function_class_ ( IO_WORKITEM_ROUTINE )
_IRQL_requires_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClassLogProvisioningTypeChangedEvent (
PDEVICE_OBJECT DeviceObject ,
PVOID Context
)
/*
Routine Description :
This function logs a provisioning type changed event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the provisioning type change .
Context : A pointer to the IO_WORKITEM in which this function is running .
- - */
{
PIO_WORKITEM workItem = ( PIO_WORKITEM ) Context ;
if ( NT_SUCCESS ( ClasspLogSystemEventWithDeviceNumber ( DeviceObject , IO_WARNING_DISK_PROVISIONING_TYPE_CHANGED ) ) ) {
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassLogProvisioningTypeChangedEvent: DO (%p), LB Provisioning Type changed logged. \n " ,
DeviceObject ) ) ;
} else {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassLogProvisioningTypeChangedEvent: DO (%p), Failed to allocate memory for error log entry. \n " ,
DeviceObject ) ) ;
}
ClassReleaseRemoveLock ( DeviceObject , ( PIRP ) workItem ) ;
IoFreeWorkItem ( workItem ) ;
}
VOID
ClassQueueProvisioningTypeChangedEventWorker (
_In_ PDEVICE_OBJECT DeviceObject
)
/*
Routine Description :
This function queues a delayed work item that will eventually log a
provisioning type changed event to the system event log .
Arguments :
DeviceObject : The FDO that represents the device that reported the provisioning type change .
- - */
{
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PIO_WORKITEM workItem = NULL ;
if ( commonExtension - > IsFdo )
{
workItem = IoAllocateWorkItem ( DeviceObject ) ;
if ( workItem )
{
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Queueing LB provisioning type changed event work item. \n " ,
DeviceObject ) ) ;
ClassAcquireRemoveLock ( DeviceObject , ( PIRP ) ( workItem ) ) ;
//
// Queue a work item to write the threshold notification to the
// system event log.
//
IoQueueWorkItem ( workItem , ClassLogProvisioningTypeChangedEvent , DelayedWorkQueue , workItem ) ;
}
else
{
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Failed to allocate memory for the work item. \n " ,
DeviceObject ) ) ;
}
}
}
_Function_class_ ( IO_WORKITEM_ROUTINE )
_IRQL_requires_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ClasspLogIOEventWithContext (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_opt_ PVOID Context
)
/*
Routine Description :
This function logs an event to the system event log with dumpdata containing opcode and
sense data .
Arguments :
DeviceObject : The FDO that represents the device that retried the IO .
Context : A pointer to the OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT that has data to be logged as part of the message .
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = ( POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ) Context ;
PIO_WORKITEM workItem ;
PIO_ERROR_LOG_PACKET errorLogEntry = NULL ;
ULONG logEntrySize ;
PWCHAR stringIndex = NULL ;
LONG stringSize = 0 ;
ULONG senseBufferSize ;
ULONG stringsBufferLength = 0 ;
ULONG pdoNameLength = 0 ;
NT_ASSERT ( ioLogMessageContextHeader ! = NULL ) ;
_Analysis_assume_ ( ioLogMessageContextHeader ! = NULL ) ;
switch ( ioLogMessageContextHeader - > ErrorCode ) {
case IO_ERROR_IO_HARDWARE_ERROR :
case IO_WARNING_IO_OPERATION_RETRIED : {
//
// We need to allocate enough space for 3 insertion strings:
// 1. A ULONGLONG in Hex representing the LBA which means a max of 16 digits,
// plus two for "0x" plus one for the NULL character.
// 2. A ULONG representing the disk number in decimal, which means
// a max of 10 digits, plus one for the NULL character.
// 3. The PDO name, so that if the disk number is hidden from the
// user for some reason, there is still a way to associate the
// event with the correct device.
//
stringsBufferLength = ( 19 + 11 ) * sizeof ( WCHAR ) ;
//
// Query for the size of the PDO name.
//
status = IoGetDeviceProperty ( fdoExtension - > LowerPdo ,
DevicePropertyPhysicalDeviceObjectName ,
0 ,
NULL ,
& pdoNameLength ) ;
if ( status = = STATUS_BUFFER_TOO_SMALL & & pdoNameLength > 0 ) {
stringsBufferLength + = pdoNameLength ;
} else {
pdoNameLength = 0 ;
}
break ;
}
}
workItem = ioLogMessageContextHeader - > WorkItem ;
//
// DumpData[0] which is of ULONG size and will contain opcode|srbstatus|scsistatus.
// Then we will have sensebuffer, hence
// DumpDataSize = senseBufferSize + sizeof(ULONG)
// and DumpDataSize must be multiple of sizeof(ULONG)
// which means senseBufferSize needs to ULONG aligned
// Please note we will use original buffersize for padding later
//
senseBufferSize = ALIGN_UP_BY ( ioLogMessageContextHeader - > SenseDataSize , sizeof ( ULONG ) ) ;
logEntrySize = FIELD_OFFSET ( IO_ERROR_LOG_PACKET , DumpData ) + sizeof ( ULONG ) + senseBufferSize ;
//
// We need to make sure the string offset is WCHAR-aligned (the insertion strings
// come after the sense buffer in the dump data, if any).
// But we don't need to do anything special for it,
// since FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData) is currently ULONG aligned
// and SenseBufferSize is also ULONG aligned. This means buffer that precedes the insertion string is ULONG aligned
// note stringoffset = FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + DumpDataSize
// This leads us to fact that stringoffset will always be ULONG aligned and effectively WCHAR aligned
//
//
// We need to allocate enough space for the insertion strings provided in the passed in Context
// as well as the opcode and the sense data, while making sure we cap at max error log size.
// The log packet is followed by the opcode, then the sense data, and then the
// insertion strings.
//
logEntrySize = logEntrySize + stringsBufferLength ;
if ( logEntrySize > ERROR_LOG_MAXIMUM_SIZE ) {
if ( senseBufferSize ) {
if ( logEntrySize - ERROR_LOG_MAXIMUM_SIZE < senseBufferSize ) {
//
// In below steps, senseBufferSize will become same or less than as ioLogMessageContextHeader->SenseDataSize
// it can't be more than that.
//
senseBufferSize - = logEntrySize - ERROR_LOG_MAXIMUM_SIZE ;
//
// Decrease the sensebuffersize further, if needed, to keep senseBufferSize ULONG aligned
//
senseBufferSize = ALIGN_DOWN_BY ( senseBufferSize , sizeof ( ULONG ) ) ;
} else {
senseBufferSize = 0 ;
}
}
logEntrySize = ERROR_LOG_MAXIMUM_SIZE ;
}
errorLogEntry = ( PIO_ERROR_LOG_PACKET ) IoAllocateErrorLogEntry ( DeviceObject , ( UCHAR ) logEntrySize ) ;
if ( errorLogEntry ) {
RtlZeroMemory ( errorLogEntry , logEntrySize ) ;
errorLogEntry - > MajorFunctionCode = IRP_MJ_SCSI ;
errorLogEntry - > RetryCount = 1 ;
errorLogEntry - > DumpDataSize = ( USHORT ) ( sizeof ( ULONG ) + senseBufferSize ) ;
errorLogEntry - > StringOffset = ( USHORT ) ( FIELD_OFFSET ( IO_ERROR_LOG_PACKET , DumpData ) + errorLogEntry - > DumpDataSize ) ;
errorLogEntry - > ErrorCode = ioLogMessageContextHeader - > ErrorCode ;
errorLogEntry - > DumpData [ 0 ] = ( ( ( ULONG ) ( ioLogMessageContextHeader - > OpCode ) ) < < 24 ) |
( ( ( ULONG ) ( ioLogMessageContextHeader - > SrbStatus ) ) < < 16 ) |
( ( ( ULONG ) ( ioLogMessageContextHeader - > ScsiStatus ) ) < < 8 ) ;
//
// Copy sense data and do padding for sense data if needed, with '-'
//
if ( senseBufferSize > ioLogMessageContextHeader - > SenseDataSize ) {
RtlCopyMemory ( & errorLogEntry - > DumpData [ 1 ] , ioLogMessageContextHeader - > SenseData , ioLogMessageContextHeader - > SenseDataSize ) ;
RtlFillMemory ( ( PCHAR ) & errorLogEntry - > DumpData [ 1 ] + ioLogMessageContextHeader - > SenseDataSize , ( senseBufferSize - ioLogMessageContextHeader - > SenseDataSize ) , ' - ' ) ;
} else {
RtlCopyMemory ( & errorLogEntry - > DumpData [ 1 ] , ioLogMessageContextHeader - > SenseData , senseBufferSize ) ;
}
stringIndex = ( PWCHAR ) ( ( PCHAR ) errorLogEntry - > DumpData + errorLogEntry - > DumpDataSize ) ;
stringSize = logEntrySize - errorLogEntry - > StringOffset ;
//
// Add the strings
//
switch ( ioLogMessageContextHeader - > ErrorCode ) {
case IO_ERROR_IO_HARDWARE_ERROR :
case IO_WARNING_IO_OPERATION_RETRIED : {
PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = ( PIO_RETRIED_LOG_MESSAGE_CONTEXT ) Context ;
//
// The first is a "0x" plus ULONGLONG in hex representing the LBA plus the NULL character.
// The second is a ULONG representing the disk number plus the NULL character.
//
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " 0x%I64x " , ioLogMessageContext - > Lba . QuadPart ) ;
if ( NT_SUCCESS ( status ) ) {
errorLogEntry - > NumberOfStrings + + ;
//
// Add the disk number to the insertion strings.
//
stringSize - = ( ULONG ) ( wcslen ( stringIndex ) + 1 ) * sizeof ( WCHAR ) ;
stringIndex + = ( wcslen ( stringIndex ) + 1 ) ;
if ( stringSize > 0 ) {
status = RtlStringCbPrintfW ( stringIndex , stringSize , L " %d " , ioLogMessageContext - > DeviceNumber ) ;
if ( NT_SUCCESS ( status ) ) {
errorLogEntry - > NumberOfStrings + + ;
stringSize - = ( ULONG ) ( wcslen ( stringIndex ) + 1 ) * sizeof ( WCHAR ) ;
stringIndex + = ( wcslen ( stringIndex ) + 1 ) ;
if ( stringSize > = ( LONG ) pdoNameLength & & pdoNameLength > 0 ) {
ULONG resultLength ;
//
// Get the PDO name and place it in the insertion string buffer.
//
status = IoGetDeviceProperty ( fdoExtension - > LowerPdo ,
DevicePropertyPhysicalDeviceObjectName ,
pdoNameLength ,
stringIndex ,
& resultLength ) ;
if ( NT_SUCCESS ( status ) & & resultLength > 0 ) {
errorLogEntry - > NumberOfStrings + + ;
}
}
}
}
}
break ;
}
}
//
// Write the error log packet to the system error logging thread.
// It will be freed automatically.
//
IoWriteErrorLogEntry ( errorLogEntry ) ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspLogIORetriedEvent: DO (%p), Soft threshold notification logged. \n " ,
DeviceObject ) ) ;
}
ClassReleaseRemoveLock ( DeviceObject , ( PIRP ) workItem ) ;
if ( ioLogMessageContextHeader - > SenseData ) {
ExFreePool ( ioLogMessageContextHeader - > SenseData ) ;
}
if ( workItem ) {
IoFreeWorkItem ( workItem ) ;
}
ExFreePool ( ioLogMessageContextHeader ) ;
}
VOID
ClasspQueueLogIOEventWithContextWorker (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ ULONG SenseBufferSize ,
_In_ PVOID SenseData ,
_In_ UCHAR SrbStatus ,
_In_ UCHAR ScsiStatus ,
_In_ ULONG ErrorCode ,
_In_ ULONG CdbLength ,
_In_opt_ PCDB Cdb ,
_In_opt_ PTRANSFER_PACKET Pkt
)
/*
Routine Description :
Helper function that queues a delayed work item that will eventually
log an event to the system event log corresponding to passed in ErrorCode .
The dumpdata is fixed to include the opcode and the sense information .
But the number of insertion strings varies based on the passed in ErrorCode .
Arguments :
DeviceObject : The FDO that represents the device that was the target of the IO .
SesneBufferSize : Size of the SenseData buffer .
SenseData : Error information from the target ( to be included in the dump data ) .
SrbStatus : Srb status returned by the miniport .
ScsiStatus : SCSI status associated with the request upon completion from lower layers .
ErrorCode : Numerical value of the error code .
CdbLength : Number of bytes of Cdb .
Cdb : Pointer to the CDB .
Pkt : The tranfer packet representing the IO of interest . This may be NULL .
- - */
{
PCOMMON_DEVICE_EXTENSION commonExtension = ( PCOMMON_DEVICE_EXTENSION ) ( DeviceObject - > DeviceExtension ) ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = NULL ;
PVOID senseData = NULL ;
PIO_WORKITEM workItem = NULL ;
ULONG senseBufferSize = 0 ;
LARGE_INTEGER lba = { 0 } ;
if ( ! commonExtension - > IsFdo ) {
return ;
}
if ( ! Cdb ) {
return ;
}
workItem = IoAllocateWorkItem ( DeviceObject ) ;
if ( ! workItem ) {
goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage ;
}
if ( SenseBufferSize ) {
senseData = ExAllocatePoolWithTag ( NonPagedPoolNx , SenseBufferSize , CLASSPNP_POOL_TAG_LOG_MESSAGE ) ;
if ( senseData ) {
senseBufferSize = SenseBufferSize ;
}
}
if ( CdbLength = = 16 ) {
REVERSE_BYTES_QUAD ( & lba , Cdb - > CDB16 . LogicalBlock ) ;
} else {
( ( PFOUR_BYTE ) & lba . LowPart ) - > Byte3 = Cdb - > CDB10 . LogicalBlockByte0 ;
( ( PFOUR_BYTE ) & lba . LowPart ) - > Byte2 = Cdb - > CDB10 . LogicalBlockByte1 ;
( ( PFOUR_BYTE ) & lba . LowPart ) - > Byte1 = Cdb - > CDB10 . LogicalBlockByte2 ;
( ( PFOUR_BYTE ) & lba . LowPart ) - > Byte0 = Cdb - > CDB10 . LogicalBlockByte3 ;
}
//
// Calculate the amount of buffer required for the insertion strings.
//
switch ( ErrorCode ) {
case IO_ERROR_IO_HARDWARE_ERROR :
case IO_WARNING_IO_OPERATION_RETRIED : {
PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = NULL ;
ioLogMessageContext = ExAllocatePoolWithTag ( NonPagedPoolNx , sizeof ( IO_RETRIED_LOG_MESSAGE_CONTEXT ) , CLASSPNP_POOL_TAG_LOG_MESSAGE ) ;
if ( ! ioLogMessageContext ) {
goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage ;
}
ioLogMessageContext - > Lba . QuadPart = lba . QuadPart ;
ioLogMessageContext - > DeviceNumber = fdoExtension - > DeviceNumber ;
ioLogMessageContextHeader = ( POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ) ioLogMessageContext ;
break ;
}
default : goto __ClasspQueueLogIOEventWithContextWorker_Exit ;
}
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_GENERAL ,
" ClasspQueueLogIOEventWithContextWorker: DO (%p), Pkt (%p), Queueing IO retried event log message work item. \n " ,
DeviceObject ,
Pkt ) ) ;
ioLogMessageContextHeader - > WorkItem = workItem ;
if ( senseData ) {
RtlCopyMemory ( senseData , SenseData , SenseBufferSize ) ;
}
ioLogMessageContextHeader - > SenseData = senseData ;
ioLogMessageContextHeader - > SenseDataSize = senseBufferSize ;
ioLogMessageContextHeader - > SrbStatus = SrbStatus ;
ioLogMessageContextHeader - > ScsiStatus = ScsiStatus ;
ioLogMessageContextHeader - > OpCode = Cdb - > CDB6GENERIC . OperationCode ;
ioLogMessageContextHeader - > Reserved = 0 ;
ioLogMessageContextHeader - > ErrorCode = ErrorCode ;
ClassAcquireRemoveLock ( DeviceObject , ( PIRP ) ( workItem ) ) ;
//
// Queue a work item to write the system event log.
//
IoQueueWorkItem ( workItem , ClasspLogIOEventWithContext , DelayedWorkQueue , ioLogMessageContextHeader ) ;
return ;
__ClasspQueueLogIOEventWithContextWorker_ExitWithMessage :
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_GENERAL ,
" ClasspQueueLogIOEventWithContextWorker: DO (%p), Failed to allocate memory for the log message. \n " ,
DeviceObject ) ) ;
__ClasspQueueLogIOEventWithContextWorker_Exit :
if ( senseData ) {
ExFreePool ( senseData ) ;
}
if ( workItem ) {
IoFreeWorkItem ( workItem ) ;
}
if ( ioLogMessageContextHeader ) {
ExFreePool ( ioLogMessageContextHeader ) ;
}
}
2020-12-05 17:52:13 +00:00
static
2020-08-29 03:06:22 +00:00
BOOLEAN
ValidPersistentReserveScope (
UCHAR Scope )
{
switch ( Scope ) {
case RESERVATION_SCOPE_LU :
case RESERVATION_SCOPE_ELEMENT :
return TRUE ;
default :
break ;
}
return FALSE ;
}
2020-12-05 17:52:13 +00:00
static
2020-08-29 03:06:22 +00:00
BOOLEAN
ValidPersistentReserveType (
UCHAR Type )
{
switch ( Type ) {
case RESERVATION_TYPE_WRITE_EXCLUSIVE :
case RESERVATION_TYPE_EXCLUSIVE :
case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS :
case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS :
return TRUE ;
default :
break ;
}
return FALSE ;
}
/*++
ClasspPersistentReserve
Routine Description :
Handles IOCTL_STORAGE_PERSISTENT_RESERVE_IN and IOCTL_STORAGE_PERSISTENT_RESERVE_OUT .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Srb - pointer to preallocated SCSI_REQUEST_BLOCK .
Return Value :
Status Code
- - */
NTSTATUS
ClasspPersistentReserve (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PCDB cdb = NULL ;
PPERSISTENT_RESERVE_COMMAND prCommand = Irp - > AssociatedIrp . SystemBuffer ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
NTSTATUS status ;
ULONG dataBufLen ;
ULONG controlCode = irpStack - > Parameters . DeviceIoControl . IoControlCode ;
BOOLEAN writeToDevice ;
//
// Check common input buffer parameters.
//
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength <
sizeof ( PERSISTENT_RESERVE_COMMAND ) | |
prCommand - > Size < sizeof ( PERSISTENT_RESERVE_COMMAND ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
Irp - > IoStatus . Status = status ;
Irp - > IoStatus . Information = 0 ;
FREE_POOL ( Srb ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
goto ClasspPersistentReserve_Exit ;
}
//
// Check buffer alignment. Only an issue if another kernel mode component
// (not the I/O manager) allocates the buffer.
//
if ( ( ULONG_PTR ) prCommand & fdoExtension - > AdapterDescriptor - > AlignmentMask ) {
status = STATUS_INVALID_USER_BUFFER ;
Irp - > IoStatus . Status = status ;
Irp - > IoStatus . Information = 0 ;
FREE_POOL ( Srb ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
goto ClasspPersistentReserve_Exit ;
}
//
// Check additional parameters.
//
status = STATUS_SUCCESS ;
SrbSetCdbLength ( Srb , 10 ) ;
cdb = SrbGetCdb ( Srb ) ;
if ( controlCode = = IOCTL_STORAGE_PERSISTENT_RESERVE_IN ) {
//
// Check output buffer for PR In.
//
if ( irpStack - > Parameters . DeviceIoControl . OutputBufferLength <
prCommand - > PR_IN . AllocationLength ) {
status = STATUS_INVALID_PARAMETER ;
}
switch ( prCommand - > PR_IN . ServiceAction ) {
case RESERVATION_ACTION_READ_KEYS :
if ( prCommand - > PR_IN . AllocationLength < sizeof ( PRI_REGISTRATION_LIST ) ) {
status = STATUS_INVALID_PARAMETER ;
}
break ;
case RESERVATION_ACTION_READ_RESERVATIONS :
if ( prCommand - > PR_IN . AllocationLength < sizeof ( PRI_RESERVATION_LIST ) ) {
status = STATUS_INVALID_PARAMETER ;
}
break ;
default :
status = STATUS_INVALID_PARAMETER ;
break ;
}
if ( ! NT_SUCCESS ( status ) ) {
Irp - > IoStatus . Status = status ;
Irp - > IoStatus . Information = 0 ;
FREE_POOL ( Srb ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
goto ClasspPersistentReserve_Exit ;
}
//
// Fill in the CDB.
//
cdb - > PERSISTENT_RESERVE_IN . OperationCode = SCSIOP_PERSISTENT_RESERVE_IN ;
cdb - > PERSISTENT_RESERVE_IN . ServiceAction = prCommand - > PR_IN . ServiceAction ;
REVERSE_BYTES_SHORT ( & ( cdb - > PERSISTENT_RESERVE_IN . AllocationLength ) ,
& ( prCommand - > PR_IN . AllocationLength ) ) ;
dataBufLen = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
writeToDevice = FALSE ;
} else {
//
// Verify ServiceAction, Scope, and Type
//
switch ( prCommand - > PR_OUT . ServiceAction ) {
case RESERVATION_ACTION_REGISTER :
case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING :
case RESERVATION_ACTION_CLEAR :
// Scope and type ignored.
break ;
case RESERVATION_ACTION_RESERVE :
case RESERVATION_ACTION_RELEASE :
case RESERVATION_ACTION_PREEMPT :
case RESERVATION_ACTION_PREEMPT_ABORT :
if ( ! ValidPersistentReserveScope ( prCommand - > PR_OUT . Scope ) | |
! ValidPersistentReserveType ( prCommand - > PR_OUT . Type ) ) {
status = STATUS_INVALID_PARAMETER ;
}
break ;
default :
status = STATUS_INVALID_PARAMETER ;
break ;
}
//
// Check input buffer for PR Out.
// Caller must include the PR parameter list.
//
if ( NT_SUCCESS ( status ) ) {
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength <
( sizeof ( PERSISTENT_RESERVE_COMMAND ) +
sizeof ( PRO_PARAMETER_LIST ) ) | |
prCommand - > Size <
irpStack - > Parameters . DeviceIoControl . InputBufferLength ) {
status = STATUS_INVALID_PARAMETER ;
}
}
if ( ! NT_SUCCESS ( status ) ) {
Irp - > IoStatus . Status = status ;
Irp - > IoStatus . Information = 0 ;
FREE_POOL ( Srb ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
goto ClasspPersistentReserve_Exit ;
}
//
// Fill in the CDB.
//
cdb - > PERSISTENT_RESERVE_OUT . OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT ;
cdb - > PERSISTENT_RESERVE_OUT . ServiceAction = prCommand - > PR_OUT . ServiceAction ;
cdb - > PERSISTENT_RESERVE_OUT . Scope = prCommand - > PR_OUT . Scope ;
cdb - > PERSISTENT_RESERVE_OUT . Type = prCommand - > PR_OUT . Type ;
cdb - > PERSISTENT_RESERVE_OUT . ParameterListLength [ 1 ] = ( UCHAR ) sizeof ( PRO_PARAMETER_LIST ) ;
//
// Move the parameter list to the beginning of the data buffer (so it is aligned
// correctly and that the MDL describes it correctly).
//
RtlMoveMemory ( prCommand ,
prCommand - > PR_OUT . ParameterList ,
sizeof ( PRO_PARAMETER_LIST ) ) ;
dataBufLen = sizeof ( PRO_PARAMETER_LIST ) ;
writeToDevice = TRUE ;
}
//
// Fill in the SRB
//
//
// Set timeout value.
//
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue ) ;
//
// Send as a tagged request.
//
SrbSetRequestAttribute ( Srb , SRB_HEAD_OF_QUEUE_TAG_REQUEST ) ;
SrbSetSrbFlags ( Srb , SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE ) ;
status = ClassSendSrbAsynchronous ( DeviceObject ,
Srb ,
Irp ,
prCommand ,
dataBufLen ,
writeToDevice ) ;
ClasspPersistentReserve_Exit :
return status ;
}
/*++
ClasspPriorityHint
Routine Description :
Handles IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT .
Arguments :
DeviceObject - a pointer to the device object
Irp - a pointer to the I / O request packet
Return Value :
Status Code
- - */
NTSTATUS
ClasspPriorityHint (
PDEVICE_OBJECT DeviceObject ,
PIRP Irp
)
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension - > PrivateFdoData ;
PSTORAGE_PRIORITY_HINT_SUPPORT priSupport = Irp - > AssociatedIrp . SystemBuffer ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
NTSTATUS status = STATUS_SUCCESS ;
Irp - > IoStatus . Information = 0 ;
//
// Check whether this device supports idle priority.
//
if ( ! fdoData - > IdlePrioritySupported ) {
status = STATUS_NOT_SUPPORTED ;
goto PriorityHintExit ;
}
if ( irpStack - > Parameters . DeviceIoControl . OutputBufferLength <
sizeof ( STORAGE_PRIORITY_HINT_SUPPORT ) ) {
status = STATUS_BUFFER_TOO_SMALL ;
goto PriorityHintExit ;
}
RtlZeroMemory ( priSupport , sizeof ( STORAGE_PRIORITY_HINT_SUPPORT ) ) ;
status = ClassForwardIrpSynchronous ( commonExtension , Irp ) ;
if ( ! NT_SUCCESS ( status ) ) {
//
// If I/O priority is not supported by lower drivers, just set the
// priorities supported by class driver.
//
TracePrint ( ( TRACE_LEVEL_FATAL , TRACE_FLAG_IOCTL , " ClasspPriorityHint: I/O priority not supported by port driver. \n " ) ) ;
priSupport - > SupportFlags = 0 ;
status = STATUS_SUCCESS ;
}
TracePrint ( ( TRACE_LEVEL_FATAL , TRACE_FLAG_IOCTL , " ClasspPriorityHint: I/O priorities supported by port driver: %X \n " , priSupport - > SupportFlags ) ) ;
priSupport - > SupportFlags | = ( 1 < < IoPriorityVeryLow ) |
( 1 < < IoPriorityLow ) |
( 1 < < IoPriorityNormal ) ;
TracePrint ( ( TRACE_LEVEL_FATAL , TRACE_FLAG_IOCTL , " ClasspPriorityHint: I/O priorities supported: %X \n " , priSupport - > SupportFlags ) ) ;
Irp - > IoStatus . Information = sizeof ( STORAGE_PRIORITY_HINT_SUPPORT ) ;
PriorityHintExit :
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
/*++
ClasspConvertToScsiRequestBlock
Routine Description :
Convert an extended SRB to a SCSI_REQUEST_BLOCK . This function handles only
a single SRB and will not converted SRBs that are linked .
Arguments :
Srb - a pointer to a SCSI_REQUEST_BLOCK
SrbEx - a pointer to an extended SRB
Return Value :
None
- - */
VOID
ClasspConvertToScsiRequestBlock (
_Out_ PSCSI_REQUEST_BLOCK Srb ,
_In_ PSTORAGE_REQUEST_BLOCK SrbEx
)
{
PSTOR_ADDR_BTL8 storAddrBtl8 ;
ULONG i ;
BOOLEAN foundEntry = FALSE ;
PSRBEX_DATA srbExData ;
if ( ( Srb = = NULL ) | | ( SrbEx = = NULL ) ) {
return ;
}
RtlZeroMemory ( Srb , sizeof ( SCSI_REQUEST_BLOCK ) ) ;
Srb - > Length = sizeof ( SCSI_REQUEST_BLOCK ) ;
Srb - > Function = ( UCHAR ) SrbEx - > SrbFunction ;
Srb - > SrbStatus = SrbEx - > SrbStatus ;
Srb - > QueueTag = ( UCHAR ) SrbEx - > RequestTag ;
Srb - > QueueAction = ( UCHAR ) SrbEx - > RequestAttribute ;
Srb - > SrbFlags = SrbEx - > SrbFlags ;
Srb - > DataTransferLength = SrbEx - > DataTransferLength ;
Srb - > TimeOutValue = SrbEx - > TimeOutValue ;
Srb - > DataBuffer = SrbEx - > DataBuffer ;
Srb - > OriginalRequest = SrbEx - > OriginalRequest ;
Srb - > SrbExtension = SrbEx - > MiniportContext ;
Srb - > InternalStatus = SrbEx - > SystemStatus ;
//
// Handle address fields
//
if ( SrbEx - > AddressOffset > = sizeof ( STORAGE_REQUEST_BLOCK ) ) {
storAddrBtl8 = ( PSTOR_ADDR_BTL8 ) ( ( PCHAR ) SrbEx + SrbEx - > AddressOffset ) ;
if ( storAddrBtl8 - > Type = = STOR_ADDRESS_TYPE_BTL8 ) {
Srb - > PathId = storAddrBtl8 - > Path ;
Srb - > TargetId = storAddrBtl8 - > Target ;
Srb - > Lun = storAddrBtl8 - > Lun ;
} else {
// Catch unsupported address types
NT_ASSERT ( FALSE ) ;
}
}
//
// Handle SRB function specific fields
//
if ( SrbEx - > NumSrbExData > 0 ) {
for ( i = 0 ; i < SrbEx - > NumSrbExData ; i + + ) {
if ( ( SrbEx - > SrbExDataOffset [ i ] = = 0 ) | |
( SrbEx - > SrbExDataOffset [ i ] < sizeof ( STORAGE_REQUEST_BLOCK ) ) ) {
// Catch invalid offsets
NT_ASSERT ( FALSE ) ;
continue ;
}
srbExData = ( PSRBEX_DATA ) ( ( PCHAR ) SrbEx + SrbEx - > SrbExDataOffset [ i ] ) ;
switch ( SrbEx - > SrbFunction ) {
case SRB_FUNCTION_EXECUTE_SCSI :
switch ( srbExData - > Type ) {
case SrbExDataTypeScsiCdb16 :
Srb - > ScsiStatus = ( ( PSRBEX_DATA_SCSI_CDB16 ) srbExData ) - > ScsiStatus ;
Srb - > CdbLength = ( ( PSRBEX_DATA_SCSI_CDB16 ) srbExData ) - > CdbLength ;
Srb - > SenseInfoBufferLength = ( ( PSRBEX_DATA_SCSI_CDB16 ) srbExData ) - > SenseInfoBufferLength ;
Srb - > SenseInfoBuffer = ( ( PSRBEX_DATA_SCSI_CDB16 ) srbExData ) - > SenseInfoBuffer ;
RtlCopyMemory ( Srb - > Cdb , ( ( PSRBEX_DATA_SCSI_CDB16 ) srbExData ) - > Cdb , sizeof ( Srb - > Cdb ) ) ;
foundEntry = TRUE ;
break ;
case SrbExDataTypeScsiCdb32 :
Srb - > ScsiStatus = ( ( PSRBEX_DATA_SCSI_CDB32 ) srbExData ) - > ScsiStatus ;
Srb - > CdbLength = ( ( PSRBEX_DATA_SCSI_CDB32 ) srbExData ) - > CdbLength ;
Srb - > SenseInfoBufferLength = ( ( PSRBEX_DATA_SCSI_CDB32 ) srbExData ) - > SenseInfoBufferLength ;
Srb - > SenseInfoBuffer = ( ( PSRBEX_DATA_SCSI_CDB32 ) srbExData ) - > SenseInfoBuffer ;
// Copy only the first 16 bytes
RtlCopyMemory ( Srb - > Cdb , ( ( PSRBEX_DATA_SCSI_CDB32 ) srbExData ) - > Cdb , sizeof ( Srb - > Cdb ) ) ;
foundEntry = TRUE ;
break ;
case SrbExDataTypeScsiCdbVar :
Srb - > ScsiStatus = ( ( PSRBEX_DATA_SCSI_CDB_VAR ) srbExData ) - > ScsiStatus ;
Srb - > CdbLength = ( UCHAR ) ( ( PSRBEX_DATA_SCSI_CDB_VAR ) srbExData ) - > CdbLength ;
Srb - > SenseInfoBufferLength = ( ( PSRBEX_DATA_SCSI_CDB_VAR ) srbExData ) - > SenseInfoBufferLength ;
Srb - > SenseInfoBuffer = ( ( PSRBEX_DATA_SCSI_CDB_VAR ) srbExData ) - > SenseInfoBuffer ;
// Copy only the first 16 bytes
RtlCopyMemory ( Srb - > Cdb , ( ( PSRBEX_DATA_SCSI_CDB_VAR ) srbExData ) - > Cdb , sizeof ( Srb - > Cdb ) ) ;
foundEntry = TRUE ;
break ;
default :
break ;
}
break ;
case SRB_FUNCTION_WMI :
if ( srbExData - > Type = = SrbExDataTypeWmi ) {
( ( PSCSI_WMI_REQUEST_BLOCK ) Srb ) - > WMISubFunction = ( ( PSRBEX_DATA_WMI ) srbExData ) - > WMISubFunction ;
( ( PSCSI_WMI_REQUEST_BLOCK ) Srb ) - > WMIFlags = ( ( PSRBEX_DATA_WMI ) srbExData ) - > WMIFlags ;
( ( PSCSI_WMI_REQUEST_BLOCK ) Srb ) - > DataPath = ( ( PSRBEX_DATA_WMI ) srbExData ) - > DataPath ;
foundEntry = TRUE ;
}
break ;
case SRB_FUNCTION_PNP :
if ( srbExData - > Type = = SrbExDataTypePnP ) {
( ( PSCSI_PNP_REQUEST_BLOCK ) Srb ) - > PnPAction = ( ( PSRBEX_DATA_PNP ) srbExData ) - > PnPAction ;
( ( PSCSI_PNP_REQUEST_BLOCK ) Srb ) - > PnPSubFunction = ( ( PSRBEX_DATA_PNP ) srbExData ) - > PnPSubFunction ;
( ( PSCSI_PNP_REQUEST_BLOCK ) Srb ) - > SrbPnPFlags = ( ( PSRBEX_DATA_PNP ) srbExData ) - > SrbPnPFlags ;
foundEntry = TRUE ;
}
break ;
case SRB_FUNCTION_POWER :
if ( srbExData - > Type = = SrbExDataTypePower ) {
( ( PSCSI_POWER_REQUEST_BLOCK ) Srb ) - > DevicePowerState = ( ( PSRBEX_DATA_POWER ) srbExData ) - > DevicePowerState ;
( ( PSCSI_POWER_REQUEST_BLOCK ) Srb ) - > PowerAction = ( ( PSRBEX_DATA_POWER ) srbExData ) - > PowerAction ;
( ( PSCSI_POWER_REQUEST_BLOCK ) Srb ) - > SrbPowerFlags = ( ( PSRBEX_DATA_POWER ) srbExData ) - > SrbPowerFlags ;
foundEntry = TRUE ;
}
break ;
default :
break ;
}
//
// Quit on first match
//
if ( foundEntry ) {
break ;
}
}
}
return ;
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
NTSTATUS
ClasspGetMaximumTokenListIdentifier (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_z_ PWSTR RegistryPath ,
_Out_ PULONG MaximumListIdentifier
)
/*++
Routine Description :
This routine returns the maximum ListIdentifier ( to be used when building TokenOperation
requests ) by querying the value MaximumListIdentifier under the key ' RegistryPath ' .
Arguments :
DeviceObject - The device handling the request .
RegistryPath - The absolute registry path under which MaximumListIdentifier resides .
MaximumListIdentifier - Returns the value being queried .
Return Value :
STATUS_SUCCESS or appropriate error status returned by Registry API .
- - */
{
RTL_QUERY_REGISTRY_TABLE queryTable [ 2 ] ;
ULONG value = 0 ;
NTSTATUS status ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_PNP ,
" ClasspGetMaximumTokenListIdentifier (%p): Entering function. \n " ,
DeviceObject ) ) ;
//
// Zero the table entries.
//
RtlZeroMemory ( queryTable , sizeof ( queryTable ) ) ;
//
// The query table has two entries. One for the MaximumListIdentifier and
// the second which is the 'NULL' terminator.
//
// Indicate that there is NO call-back routine.
//
queryTable [ 0 ] . Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK ;
//
// The value to query.
//
queryTable [ 0 ] . Name = REG_MAX_LIST_IDENTIFIER_VALUE ;
//
// Where to put the value, the type of the value, default value and length.
//
queryTable [ 0 ] . EntryContext = & value ;
queryTable [ 0 ] . DefaultType = ( REG_DWORD < < RTL_QUERY_REGISTRY_TYPECHECK_SHIFT ) | REG_DWORD ;
queryTable [ 0 ] . DefaultData = & value ;
queryTable [ 0 ] . DefaultLength = sizeof ( value ) ;
//
// Try to get the maximum listIdentifier.
//
status = RtlQueryRegistryValues ( RTL_REGISTRY_ABSOLUTE ,
RegistryPath ,
queryTable ,
NULL ,
NULL ) ;
if ( NT_SUCCESS ( status ) ) {
* MaximumListIdentifier = value ;
} else {
* MaximumListIdentifier = 0 ;
}
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_PNP ,
" ClasspGetMaximumTokenListIdentifier (%p): Exiting function with status %x (maxListId %u). \n " ,
DeviceObject ,
status ,
* MaximumListIdentifier ) ) ;
return status ;
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
NTSTATUS
ClasspGetCopyOffloadMaxDuration (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_z_ PWSTR RegistryPath ,
_Out_ PULONG MaxDuration
)
/*++
Routine Description :
This routine returns the maximum time ( in seconds ) that a Copy Offload
operation should take to complete by a target .
Arguments :
DeviceObject - The device handling the request .
RegistryPath - The absolute registry path under which MaxDuration resides .
MaxDuration - Returns the value being queried , in seconds .
Return Value :
STATUS_SUCCESS or appropriate error status returned by Registry API .
- - */
{
RTL_QUERY_REGISTRY_TABLE queryTable [ 2 ] ;
ULONG value = 0 ;
NTSTATUS status ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_PNP ,
" ClasspGetCopyOffloadMaxDuration (%p): Entering function. \n " ,
DeviceObject ) ) ;
//
// Zero the table entries.
//
RtlZeroMemory ( queryTable , sizeof ( queryTable ) ) ;
//
// The query table has two entries. One for CopyOffloadMaxDuration and
// the second which is the 'NULL' terminator.
//
// Indicate that there is NO call-back routine.
//
queryTable [ 0 ] . Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK ;
//
// The value to query.
//
queryTable [ 0 ] . Name = CLASSP_REG_COPY_OFFLOAD_MAX_TARGET_DURATION ;
//
// Where to put the value, the type of the value, default value and length.
//
queryTable [ 0 ] . EntryContext = & value ;
queryTable [ 0 ] . DefaultType = ( REG_DWORD < < RTL_QUERY_REGISTRY_TYPECHECK_SHIFT ) | REG_NONE ;
queryTable [ 0 ] . DefaultData = & value ;
queryTable [ 0 ] . DefaultLength = sizeof ( value ) ;
//
// Try to get the max target duration.
//
status = RtlQueryRegistryValues ( RTL_REGISTRY_ABSOLUTE ,
RegistryPath ,
queryTable ,
NULL ,
NULL ) ;
//
// Don't allow the user to set the value to lower than the default (4s) so
// they don't break ODX functionality if they accidentally set it too low.
//
if ( NT_SUCCESS ( status ) & &
value > DEFAULT_MAX_TARGET_DURATION ) {
* MaxDuration = value ;
} else {
* MaxDuration = DEFAULT_MAX_TARGET_DURATION ;
}
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_PNP ,
" ClasspGetCopyOffloadMaxDuration (%p): Exiting function with status %x (Max Duration %u seconds). \n " ,
DeviceObject ,
status ,
* MaxDuration ) ) ;
return status ;
}
_IRQL_requires_max_ ( APC_LEVEL )
_IRQL_requires_min_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
NTSTATUS
ClasspDeviceCopyOffloadProperty (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description :
This routine returns the copy offload parameters associated with the device .
This function must be called at IRQL < DISPATCH_LEVEL .
Arguments :
DeviceObject - Supplies the device object associated with this request
Irp - The IRP to be processed
Srb - The SRB associated with the request
Return Value :
NTSTATUS code
- - */
{
NTSTATUS status ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension ;
PSTORAGE_PROPERTY_QUERY query ;
PIO_STACK_LOCATION irpStack ;
ULONG length ;
ULONG information ;
PDEVICE_COPY_OFFLOAD_DESCRIPTOR copyOffloadDescr = ( PDEVICE_COPY_OFFLOAD_DESCRIPTOR ) Irp - > AssociatedIrp . SystemBuffer ;
UNREFERENCED_PARAMETER ( Srb ) ;
PAGED_CODE ( ) ;
fdoExtension = DeviceObject - > DeviceExtension ;
query = ( PSTORAGE_PROPERTY_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
length = 0 ;
information = 0 ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Entering function. \n " ,
DeviceObject ) ) ;
//
// Check proper query type.
//
if ( query - > QueryType = = PropertyExistsQuery ) {
//
// In order to maintain consistency with the how the rest of the properties
// are handled, we shall always return success for PropertyExistsQuery.
//
status = STATUS_SUCCESS ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
} else if ( query - > QueryType ! = PropertyStandardQuery ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Unsupported query type %x for Copy Offload property. \n " ,
DeviceObject ,
query - > QueryType ) ) ;
status = STATUS_NOT_SUPPORTED ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
//
// Request validation.
// Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
//
if ( KeGetCurrentIrql ( ) > = DISPATCH_LEVEL ) {
NT_ASSERT ( KeGetCurrentIrql ( ) < DISPATCH_LEVEL ) ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Query property for Copy Offload called at incorrect IRQL. \n " ,
DeviceObject ) ) ;
status = STATUS_INVALID_LEVEL ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
length = irpStack - > Parameters . DeviceIoControl . OutputBufferLength ;
if ( length < sizeof ( DEVICE_COPY_OFFLOAD_DESCRIPTOR ) ) {
if ( length > = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ) {
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Length %u specified for Copy Offload property enough only for header. \n " ,
DeviceObject ,
length ) ) ;
information = sizeof ( STORAGE_DESCRIPTOR_HEADER ) ;
copyOffloadDescr - > Version = sizeof ( DEVICE_COPY_OFFLOAD_DESCRIPTOR ) ;
copyOffloadDescr - > Size = sizeof ( DEVICE_COPY_OFFLOAD_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Incorrect length %u specified for Copy Offload property. \n " ,
DeviceObject ,
length ) ) ;
status = STATUS_BUFFER_TOO_SMALL ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
if ( ! fdoExtension - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceRODLimits ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Command not supported on this device. \n " ,
DeviceObject ) ) ;
status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
if ( ! NT_SUCCESS ( fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ) ) {
status = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): VPD retrieval had failed with %x. \n " ,
DeviceObject ,
status ) ) ;
goto __ClasspDeviceCopyOffloadProperty_Exit ;
}
//
// Fill in the output buffer. All data is copied from the FDO extension where we
// cached Block Limits and Block Device Token Limits info when the device was first initialized.
//
RtlZeroMemory ( copyOffloadDescr , length ) ;
copyOffloadDescr - > Version = 1 ;
copyOffloadDescr - > Size = sizeof ( DEVICE_COPY_OFFLOAD_DESCRIPTOR ) ;
copyOffloadDescr - > MaximumTokenLifetime = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumInactivityTimer ;
copyOffloadDescr - > DefaultTokenLifetime = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . DefaultInactivityTimer ;
copyOffloadDescr - > MaximumTransferSize = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumTokenTransferSize ;
copyOffloadDescr - > OptimalTransferCount = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . OptimalTransferCount ;
copyOffloadDescr - > MaximumDataDescriptors = fdoExtension - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumRangeDescriptors ;
if ( NT_SUCCESS ( fdoExtension - > FunctionSupportInfo - > BlockLimitsData . CommandStatus ) ) {
copyOffloadDescr - > MaximumTransferLengthPerDescriptor = fdoExtension - > FunctionSupportInfo - > BlockLimitsData . MaximumTransferLength ;
copyOffloadDescr - > OptimalTransferLengthPerDescriptor = fdoExtension - > FunctionSupportInfo - > BlockLimitsData . OptimalTransferLength ;
copyOffloadDescr - > OptimalTransferLengthGranularity = fdoExtension - > FunctionSupportInfo - > BlockLimitsData . OptimalTransferLengthGranularity ;
}
information = sizeof ( DEVICE_COPY_OFFLOAD_DESCRIPTOR ) ;
status = STATUS_SUCCESS ;
__ClasspDeviceCopyOffloadProperty_Exit :
//
// Set the size and status in IRP
//
Irp - > IoStatus . Information = information ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspDeviceCopyOffloadProperty (%p): Exiting function with status %x. \n " ,
DeviceObject ,
status ) ) ;
return status ;
}
_IRQL_requires_max_ ( APC_LEVEL )
_IRQL_requires_min_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
NTSTATUS
ClasspValidateOffloadSupported (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp
)
/*++
Routine Description :
This routine validates if this device supports offload requests .
This function must be called at IRQL < DISPATCH_LEVEL .
Arguments :
DeviceObject - Supplies the device object associated with this request
Irp - The IRP to be processed
Return Value :
NTSTATUS code
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt ;
NTSTATUS status ;
PAGED_CODE ( ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadSupported (%p): Entering function. Irp %p \n " ,
DeviceObject ,
Irp ) ) ;
fdoExt = DeviceObject - > DeviceExtension ;
status = STATUS_SUCCESS ;
//
// For now this command is only supported by disk devices
//
if ( ( DeviceObject - > DeviceType = = FILE_DEVICE_DISK ) & &
( ! TEST_FLAG ( DeviceObject - > Characteristics , FILE_FLOPPY_DISKETTE ) ) ) {
if ( ! fdoExt - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceRODLimits ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadSupported (%p): Command not supported on this disk device. \n " ,
DeviceObject ) ) ;
status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED ;
goto __ClasspValidateOffloadSupported_Exit ;
}
if ( ! NT_SUCCESS ( fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ) ) {
status = fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ;
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadSupported (%p): VPD retrieval failed with %x. \n " ,
DeviceObject ,
status ) ) ;
goto __ClasspValidateOffloadSupported_Exit ;
}
} else {
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadSupported (%p): Suported only on Disk devices. \n " ,
DeviceObject ) ) ;
status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED ;
goto __ClasspValidateOffloadSupported_Exit ;
}
__ClasspValidateOffloadSupported_Exit :
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadSupported (%p): Exiting function Irp %p with status %x. \n " ,
DeviceObject ,
Irp ,
status ) ) ;
return status ;
}
_IRQL_requires_max_ ( APC_LEVEL )
_IRQL_requires_min_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
NTSTATUS
ClasspValidateOffloadInputParameters (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp
)
/*++
Routine Description :
This routine does some basic validation of the input parameters of the offload request .
This function must be called at IRQL < DISPATCH_LEVEL .
Arguments :
DeviceObject - Supplies the device object associated with this request
Irp - The IRP to be processed
Return Value :
NTSTATUS code
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension ;
PIO_STACK_LOCATION irpStack ;
PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes ;
PDEVICE_DATA_SET_RANGE dataSetRanges ;
ULONG dataSetRangesCount ;
ULONG i ;
NTSTATUS status ;
PAGED_CODE ( ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Entering function Irp %p. \n " ,
DeviceObject ,
Irp ) ) ;
fdoExtension = DeviceObject - > DeviceExtension ;
irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
dsmAttributes = Irp - > AssociatedIrp . SystemBuffer ;
status = STATUS_SUCCESS ;
if ( ! dsmAttributes ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): NULL DsmAttributes passed in. \n " ,
DeviceObject ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
if ( ( irpStack - > Parameters . DeviceIoControl . InputBufferLength < sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES ) ) | |
( irpStack - > Parameters . DeviceIoControl . InputBufferLength <
( sizeof ( DEVICE_MANAGE_DATA_SET_ATTRIBUTES ) + dsmAttributes - > ParameterBlockLength + dsmAttributes - > DataSetRangesLength ) ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Input buffer size (%u) too small. \n " ,
DeviceObject ,
irpStack - > Parameters . DeviceIoControl . InputBufferLength ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
if ( ( dsmAttributes - > DataSetRangesOffset = = 0 ) | |
( dsmAttributes - > DataSetRangesLength = = 0 ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges [offset %u, length %u]. \n " ,
DeviceObject ,
dsmAttributes - > DataSetRangesOffset ,
dsmAttributes - > DataSetRangesLength ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
dataSetRanges = Add2Ptr ( dsmAttributes , dsmAttributes - > DataSetRangesOffset ) ;
dataSetRangesCount = dsmAttributes - > DataSetRangesLength / sizeof ( DEVICE_DATA_SET_RANGE ) ;
if ( dataSetRangesCount = = 0 ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): DataSetRanges specifies no extents. \n " ,
DeviceObject ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
//
// Some third party disk class drivers do not query the geometry at initialization time,
// so this information may not be available at this time. If that is the case, we'll
// first query that information before proceeding with the rest of our validations.
//
if ( fdoExtension - > DiskGeometry . BytesPerSector = = 0 ) {
status = ClassReadDriveCapacity ( fdoExtension - > DeviceObject ) ;
if ( ( ! NT_SUCCESS ( status ) ) | | ( fdoExtension - > DiskGeometry . BytesPerSector = = 0 ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Couldn't retrieve disk geometry, status: %x, bytes/sector: %u. \n " ,
DeviceObject ,
status ,
fdoExtension - > DiskGeometry . BytesPerSector ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
}
//
// Data must be aligned to sector boundary and
// LengthInBytes must be > 0 for it to be a valid LBA entry
//
for ( i = 0 ; i < dataSetRangesCount ; i + + ) {
if ( ( dataSetRanges [ i ] . StartingOffset % fdoExtension - > DiskGeometry . BytesPerSector ! = 0 ) | |
( dataSetRanges [ i ] . LengthInBytes % fdoExtension - > DiskGeometry . BytesPerSector ! = 0 ) | |
( dataSetRanges [ i ] . LengthInBytes = = 0 ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges entry %u [offset %I64x, length %I64x]. \n " ,
DeviceObject ,
i ,
dataSetRanges [ i ] . StartingOffset ,
dataSetRanges [ i ] . LengthInBytes ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
if ( ( ULONGLONG ) dataSetRanges [ i ] . StartingOffset + dataSetRanges [ i ] . LengthInBytes > ( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Error! DataSetRange %u (starting LBA %I64x) specified length %I64x exceeds the medium's capacity (%I64x). \n " ,
DeviceObject ,
i ,
dataSetRanges [ i ] . StartingOffset ,
dataSetRanges [ i ] . LengthInBytes ,
fdoExtension - > CommonExtension . PartitionLength . QuadPart ) ) ;
status = STATUS_NONEXISTENT_SECTOR ;
goto __ClasspValidateOffloadInputParameters_Exit ;
}
}
__ClasspValidateOffloadInputParameters_Exit :
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspValidateOffloadInputParameters (%p): Exiting function Irp %p with status %x. \n " ,
DeviceObject ,
Irp ,
status ) ) ;
return status ;
}
_IRQL_requires_same_
NTSTATUS
ClasspGetTokenOperationCommandBufferLength (
_In_ PDEVICE_OBJECT Fdo ,
_In_ ULONG ServiceAction ,
_Inout_ PULONG CommandBufferLength ,
_Out_opt_ PULONG TokenOperationBufferLength ,
_Out_opt_ PULONG ReceiveTokenInformationBufferLength
)
/*++
Routine description :
This routine calculates the buffer length required to service a TokenOperation and its
corresponding ReceiveTokenInformation command .
Arguments :
Fdo - The functional device object processing the PopulateToken / WriteUsingToken request
ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
CommandBufferLength - Returns the length of the buffer needed to service the token request ( i . e . TokenOperation and its corresponding ReceiveTokenInformation command )
TokenOperationBufferLength - Optional parameter , which returns the length of the buffer needed to service just the TokenOperation command .
ReceiveTokenInformationBufferLength - Optional parameter , which returns the length of the buffer needed to service just the ReceiveTokenInformation command .
Return Value :
STATUS_SUCCESS
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo - > DeviceExtension ;
PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt - > PrivateFdoData ;
ULONG tokenOperationBufferLength ;
ULONG receiveTokenInformationBufferLength ;
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo - > DeviceExtension ;
PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension - > PartitionZeroExtension - > AdapterDescriptor ;
ULONG hwMaxXferLen ;
ULONG bufferLength = 0 ;
ULONG tokenOperationHeaderSize ;
ULONG responseSize ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspGetTokenOperationCommandBufferLengths (%p): Entering function. \n " ,
Fdo ) ) ;
NT_ASSERT ( fdoExt - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceRODLimits & &
NT_SUCCESS ( fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ) ) ;
if ( ServiceAction = = SERVICE_ACTION_POPULATE_TOKEN ) {
tokenOperationHeaderSize = FIELD_OFFSET ( POPULATE_TOKEN_HEADER , BlockDeviceRangeDescriptor ) ;
responseSize = FIELD_OFFSET ( RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER , TokenDescriptor ) + sizeof ( BLOCK_DEVICE_TOKEN_DESCRIPTOR ) ;
} else {
tokenOperationHeaderSize = FIELD_OFFSET ( WRITE_USING_TOKEN_HEADER , BlockDeviceRangeDescriptor ) ;
responseSize = 0 ;
}
//
// The TokenOperation command can specify a parameter length of max 2^16.
// If the device has a max limit on the number of range descriptors that can be specified in
// the TokenOperation command, we are limited to the lesser of these two values.
//
if ( fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumRangeDescriptors = = 0 ) {
tokenOperationBufferLength = MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH ;
} else {
tokenOperationBufferLength = MIN ( tokenOperationHeaderSize + fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumRangeDescriptors * sizeof ( BLOCK_DEVICE_RANGE_DESCRIPTOR ) ,
MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH ) ;
}
//
// The ReceiveTokenInformation command can specify a parameter length of max 2 ^ 32
// Also, since the sense data can be of variable size, we'll use MAX_SENSE_BUFFER_SIZE.
//
receiveTokenInformationBufferLength = MIN ( FIELD_OFFSET ( RECEIVE_TOKEN_INFORMATION_HEADER , SenseData ) + MAX_SENSE_BUFFER_SIZE + responseSize ,
MAX_RECEIVE_TOKEN_INFORMATION_PARAMETER_DATA_LENGTH ) ;
//
// Since we're going to reuse the buffer for both the TokenOperation and the ReceiveTokenInformation
// commands, the buffer length needs to handle both operations.
//
bufferLength = MAX ( tokenOperationBufferLength , receiveTokenInformationBufferLength ) ;
//
// The buffer length needs to be further limited to the adapter's capability though.
//
hwMaxXferLen = MIN ( fdoData - > HwMaxXferLen , adapterDesc - > MaximumTransferLength ) ;
bufferLength = MIN ( bufferLength , hwMaxXferLen ) ;
* CommandBufferLength = bufferLength ;
if ( TokenOperationBufferLength ) {
* TokenOperationBufferLength = tokenOperationBufferLength ;
}
if ( ReceiveTokenInformationBufferLength ) {
* ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength ;
}
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspGetTokenOperationCommandBufferLengths (%p): Exiting function with bufferLength %u (tokenOpBufLen %u, recTokenInfoBufLen %u). \n " ,
Fdo ,
bufferLength ,
tokenOperationBufferLength ,
receiveTokenInformationBufferLength ) ) ;
return STATUS_SUCCESS ;
}
_IRQL_requires_same_
NTSTATUS
ClasspGetTokenOperationDescriptorLimits (
_In_ PDEVICE_OBJECT Fdo ,
_In_ ULONG ServiceAction ,
_In_ ULONG MaxParameterBufferLength ,
_Out_ PULONG MaxBlockDescriptorsCount ,
_Out_ PULONGLONG MaxBlockDescriptorsLength
)
/*++
Routine description :
This routine calculates the maximum block descriptors and the maximum token transfer size
that can be accomodated in a single TokenOperation command .
Arguments :
Fdo - The functional device object processing the PopulateToken / WriteUsingToken request
ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
MaxParameterBufferLength - The length constraint of the entire buffer for the parameter list based on other limitations ( e . g . adapter max transfer length )
MaxBlockDescriptorsCount - Returns the maximum number of the block range descriptors that can be passed in a single TokenOperation command .
MaxBlockDescriptorsLength - Returns the maximum cumulative number of blocks across all the descriptors that must not be exceeded in a single TokenOperation command .
Return Value :
STATUS_SUCCESS
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo - > DeviceExtension ;
ULONG tokenOperationHeaderSize = ( ServiceAction = = SERVICE_ACTION_POPULATE_TOKEN ) ?
FIELD_OFFSET ( POPULATE_TOKEN_HEADER , BlockDeviceRangeDescriptor ) :
FIELD_OFFSET ( WRITE_USING_TOKEN_HEADER , BlockDeviceRangeDescriptor ) ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspGetTokenOperationDescriptorLimits (%p): Entering function. \n " ,
Fdo ) ) ;
NT_ASSERT ( fdoExt - > FunctionSupportInfo - > ValidInquiryPages . BlockDeviceRODLimits & &
NT_SUCCESS ( fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . CommandStatus ) ) ;
* MaxBlockDescriptorsCount = ( MaxParameterBufferLength - tokenOperationHeaderSize ) / sizeof ( BLOCK_DEVICE_RANGE_DESCRIPTOR ) ;
* MaxBlockDescriptorsLength = ( fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumTokenTransferSize = = 0 ) ?
MAX_TOKEN_TRANSFER_SIZE : fdoExt - > FunctionSupportInfo - > BlockDeviceRODLimitsData . MaximumTokenTransferSize ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspGetTokenOperationDescriptorLimits (%p): Exiting function with MaxDescr %u, MaxXferBlocks %I64u. \n " ,
Fdo ,
* MaxBlockDescriptorsCount ,
* MaxBlockDescriptorsLength ) ) ;
return STATUS_SUCCESS ;
}
_IRQL_requires_max_ ( APC_LEVEL )
_IRQL_requires_min_ ( PASSIVE_LEVEL )
_IRQL_requires_same_
VOID
ClasspConvertDataSetRangeToBlockDescr (
_In_ PDEVICE_OBJECT Fdo ,
_In_ PVOID BlockDescr ,
_Inout_ PULONG CurrentBlockDescrIndex ,
_In_ ULONG MaxBlockDescrCount ,
_Inout_ PULONG CurrentLbaCount ,
_In_ ULONGLONG MaxLbaCount ,
_Inout_ PDEVICE_DATA_SET_RANGE DataSetRange ,
_Inout_ PULONGLONG TotalSectorsProcessed
)
/*++
Routine Description :
Convert DEVICE_DATA_SET_RANGE entry to be WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries .
As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits ( bytes )
and LbaCount field in WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR structure is 32 bits ( sectors ) ,
it ' s possible that one DEVICE_DATA_SET_RANGE entry needs multiple
WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries . This routine handles the need for that
potential split .
Arguments :
Fdo - The functional device object
BlockDescr - Pointer to the start of the Token Operation command ' s block descriptor
CurrentBlockDescrIndex - Index into the block descriptor at which to update the DataSetRange info
It also gets updated to return the index to the next empty one .
MaxBlockDescrCount - Maximum number of block descriptors that the device can handle in a single TokenOperation command
CurrentLbaCount - Returns the LBA of the last successfully processed DataSetRange
MaxLbaCount - Maximum transfer size that the device is capable of handling in a single TokenOperation command
DataSetRange - Contains information about one range extent that needs to be converted into a block descriptor
TotalSectorsProcessed - Returns the number of sectors corresponding to the DataSetRange that were succesfully mapped into block descriptors
Return Value :
Nothing .
NOTE : if LengthInBytes does not reach to 0 , the conversion for DEVICE_DATA_SET_RANGE entry
is not completed . Further conversion is needed by calling this function again .
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension ;
PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescr ;
ULONGLONG startingSector ;
ULONGLONG sectorCount ;
ULONGLONG totalSectorCount ;
ULONGLONG numberOfOptimalChunks ;
USHORT optimalLbaPerDescrGranularity ;
ULONG optimalLbaPerDescr ;
ULONG maxLbaPerDescr ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspConvertDataSetRangeToBlockDescr (%p): Entering function. Starting offset %I64x. \n " ,
Fdo ,
DataSetRange - > StartingOffset ) ) ;
fdoExtension = Fdo - > DeviceExtension ;
blockDescr = ( PBLOCK_DEVICE_RANGE_DESCRIPTOR ) BlockDescr ;
totalSectorCount = 0 ;
//
// Since the OptimalTransferLength and the MaximumTransferLength are overloaded parameters for
// offloaded data transfers and regular read/write requests, it is not safe to use these values
// as they may report back what is used by regular read/write, which will cause a perf degradation
// in the offloaded case, since we may end up limiting the per block range descriptor length
// specified as opposed to what the target can actually handle in a single request.
// So until the SPC spec introduces these values specific to offloaded data transfers, we shall
// ignore them completely. The expectation we have from the target is as follows:
// 1. If the length specified in any of the block range descriptors is greater than the OTL that
// applies to ODX, the target will internally split into additional descriptors.
// 2. If the above causes it to run out of descriptors, or if the length specified in any of the
// descriptors is greater than the MTL that applies to ODX, the target will operate on as much
// data as possible and truncate the request to that point.
//
optimalLbaPerDescrGranularity = 0 ;
optimalLbaPerDescr = 0 ;
maxLbaPerDescr = 0 ;
if ( optimalLbaPerDescr & & maxLbaPerDescr ) {
NT_ASSERT ( optimalLbaPerDescr < = maxLbaPerDescr ) ;
}
while ( ( DataSetRange - > LengthInBytes > 0 ) & &
( * CurrentBlockDescrIndex < MaxBlockDescrCount ) & &
( * CurrentLbaCount < MaxLbaCount ) ) {
startingSector = ( ULONGLONG ) ( DataSetRange - > StartingOffset / fdoExtension - > DiskGeometry . BytesPerSector ) ;
//
// Since the block descriptor has only 4 bytes for the number of logical blocks, we are
// constrained by that theoretical maximum.
//
sectorCount = MIN ( DataSetRange - > LengthInBytes / fdoExtension - > DiskGeometry . BytesPerSector ,
MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR ) ;
//
// We are constrained by MaxLbaCount.
//
if ( ( ( ULONGLONG ) * CurrentLbaCount + sectorCount ) > = MaxLbaCount ) {
sectorCount = MaxLbaCount - * CurrentLbaCount ;
}
//
// For each descriptor, the block count should be lesser than the MaximumTransferSize
//
if ( maxLbaPerDescr > 0 ) {
//
// Each block device range descriptor can specify a max number of LBAs
//
sectorCount = MIN ( sectorCount , maxLbaPerDescr ) ;
}
//
// If the number of LBAs specified in the descriptor is greater than the OptimalTransferLength,
// processing of this descriptor by the target may incur a significant delay.
// So in order to allow the target to perform optimally, we'll further limit the number
// of blocks specified in any descriptor to be maximum OptimalTranferLength.
//
if ( optimalLbaPerDescr > 0 ) {
sectorCount = MIN ( sectorCount , optimalLbaPerDescr ) ;
}
//
// In addition, it should either be an exact multiple of the OptimalTransferLengthGranularity,
// or be lesser than the OptimalTransferLengthGranularity (taken care of here).
//
if ( optimalLbaPerDescrGranularity > 0 ) {
numberOfOptimalChunks = sectorCount / optimalLbaPerDescrGranularity ;
if ( numberOfOptimalChunks > 0 ) {
sectorCount = numberOfOptimalChunks * optimalLbaPerDescrGranularity ;
}
}
NT_ASSERT ( sectorCount < = MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR ) ;
REVERSE_BYTES_QUAD ( blockDescr [ * CurrentBlockDescrIndex ] . LogicalBlockAddress , & startingSector ) ;
REVERSE_BYTES ( blockDescr [ * CurrentBlockDescrIndex ] . TransferLength , & sectorCount ) ;
totalSectorCount + = sectorCount ;
DataSetRange - > StartingOffset + = sectorCount * fdoExtension - > DiskGeometry . BytesPerSector ;
DataSetRange - > LengthInBytes - = sectorCount * fdoExtension - > DiskGeometry . BytesPerSector ;
* CurrentBlockDescrIndex + = 1 ;
* CurrentLbaCount + = ( ULONG ) sectorCount ;
TracePrint ( ( TRACE_LEVEL_INFORMATION ,
TRACE_FLAG_IOCTL ,
" ClasspConvertDataSetRangeToBlockDescr (%p): Descriptor: %u, starting LBA: %I64x, length: %I64x bytes, media size: %I64x. \n " ,
Fdo ,
* CurrentBlockDescrIndex - 1 ,
startingSector ,
sectorCount * fdoExtension - > DiskGeometry . BytesPerSector ,
( ULONGLONG ) fdoExtension - > CommonExtension . PartitionLength . QuadPart ) ) ;
}
* TotalSectorsProcessed = totalSectorCount ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspConvertDataSetRangeToBlockDescr (%p): Exiting function (starting offset %I64x). Total sectors processed %I64u. \n " ,
Fdo ,
DataSetRange - > StartingOffset ,
totalSectorCount ) ) ;
return ;
}
_IRQL_requires_same_
PUCHAR
ClasspBinaryToAscii (
_In_reads_ ( Length ) PUCHAR HexBuffer ,
_In_ ULONG Length ,
_Inout_ PULONG UpdateLength
)
/*++
Routine Description :
This routine will convert HexBuffer into an ascii NULL - terminated string .
Note : This routine will allocate memory for storing the ascii string . It is
the responsibility of the caller to free this buffer .
Arguments :
HexBuffer - Pointer to the binary data .
Length - Length , in bytes , of HexBuffer .
UpdateLength - Storage to place the actual length of the returned string .
Return Value :
ASCII string equivalent of the hex buffer , or NULL if an error occurred .
- - */
{
static const UCHAR integerTable [ ] = { ' 0 ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' , ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' } ;
ULONG i ;
ULONG j ;
ULONG actualLength ;
PUCHAR buffer = NULL ;
UCHAR highWord ;
UCHAR lowWord ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspBinaryToAscii (HexBuff %p): Entering function. \n " ,
HexBuffer ) ) ;
if ( ! HexBuffer | | Length = = 0 ) {
* UpdateLength = 0 ;
goto __ClasspBinaryToAscii_Exit ;
}
//
// Each byte converts into 2 chars:
// e.g. 0x05 => '0' '5'
// 0x0C => '0' 'C'
// 0x12 => '1' '2'
// And we need a terminating NULL for the string.
//
actualLength = ( Length * 2 ) + 1 ;
//
// Allocate the buffer.
//
buffer = ExAllocatePoolWithTag ( NonPagedPoolNx , actualLength , CLASSPNP_POOL_TAG_TOKEN_OPERATION ) ;
if ( ! buffer ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspBinaryToAscii (HexBuff %p): Failed to allocate buffer for ASCII equivalent. \n " ,
HexBuffer ) ) ;
* UpdateLength = 0 ;
goto __ClasspBinaryToAscii_Exit ;
}
RtlZeroMemory ( buffer , actualLength ) ;
for ( i = 0 , j = 0 ; i < Length ; i + + ) {
//
// Split out each nibble from the binary byte.
//
highWord = HexBuffer [ i ] > > 4 ;
lowWord = HexBuffer [ i ] & 0x0F ;
//
// Using the lookup table, convert and stuff into
// the ascii buffer.
//
buffer [ j + + ] = integerTable [ highWord ] ;
# ifdef _MSC_VER
# pragma warning(suppress: 6386) // PREFast bug means it doesn't see that Length < actualLength
# endif
buffer [ j + + ] = integerTable [ lowWord ] ;
}
//
// Update the caller's length field.
//
* UpdateLength = actualLength ;
__ClasspBinaryToAscii_Exit :
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspBinaryToAscii (HexBuff %p): Exiting function with buffer %s. \n " ,
HexBuffer ,
( buffer = = NULL ) ? " " : ( const char * ) buffer ) ) ;
return buffer ;
}
_IRQL_requires_same_
NTSTATUS
ClasspStorageEventNotification (
_In_ PDEVICE_OBJECT DeviceObject ,
_In_ PIRP Irp
)
/*++
Routine Description :
This routine handles an asynchronous event notification ( most likely from
port drivers ) . Currently , we only care about media status change events .
Arguments :
DeviceObject - Supplies the device object associated with this request
Irp - The IRP to be processed
Return Value :
NTSTATUS code
- - */
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension ;
PIO_STACK_LOCATION irpStack ;
PSTORAGE_EVENT_NOTIFICATION storageEvents ;
NTSTATUS status ;
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspStorageEventNotification (%p): Entering function Irp %p. \n " ,
DeviceObject ,
Irp ) ) ;
fdoExtension = DeviceObject - > DeviceExtension ;
irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
storageEvents = Irp - > AssociatedIrp . SystemBuffer ;
status = STATUS_SUCCESS ;
if ( ! storageEvents ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspStorageEventNotification (%p): NULL storage events passed in. \n " ,
DeviceObject ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspStorageEventNotification_Exit ;
}
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength < sizeof ( STORAGE_EVENT_NOTIFICATION ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspStorageEventNotification (%p): Input buffer size (%u) too small. \n " ,
DeviceObject ,
irpStack - > Parameters . DeviceIoControl . InputBufferLength ) ) ;
status = STATUS_INFO_LENGTH_MISMATCH ;
goto __ClasspStorageEventNotification_Exit ;
}
if ( ( storageEvents - > Version ! = STORAGE_EVENT_NOTIFICATION_VERSION_V1 ) | |
( storageEvents - > Size ! = sizeof ( STORAGE_EVENT_NOTIFICATION ) ) ) {
TracePrint ( ( TRACE_LEVEL_ERROR ,
TRACE_FLAG_IOCTL ,
" ClasspStorageEventNotification (%p): Invalid version/size [version %u, size %u]. \n " ,
DeviceObject ,
storageEvents - > Version ,
storageEvents - > Size ) ) ;
status = STATUS_INVALID_PARAMETER ;
goto __ClasspStorageEventNotification_Exit ;
}
//
// Handle a media status event.
//
if ( storageEvents - > Events & STORAGE_EVENT_MEDIA_STATUS ) {
//
// Only initiate operation if underlying port driver supports asynchronous notification
// and this is the FDO.
//
if ( ( fdoExtension - > CommonExtension . IsFdo = = TRUE ) & &
( fdoExtension - > FunctionSupportInfo - > AsynchronousNotificationSupported ) ) {
ClassCheckMediaState ( fdoExtension ) ;
} else {
status = STATUS_NOT_SUPPORTED ;
}
}
__ClasspStorageEventNotification_Exit :
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_IOCTL ,
" ClasspStorageEventNotification (%p): Exiting function Irp %p with status %x. \n " ,
DeviceObject ,
Irp ,
status ) ) ;
Irp - > IoStatus . Information = 0 ;
Irp - > IoStatus . Status = status ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
VOID
ClasspZeroQERR (
_In_ PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description :
This routine will attempt to set the QERR bit of the mode Control page to
zero .
Arguments :
DeviceObject - Supplies the device object associated with this request
Return Value :
None
- - */
{
PMODE_PARAMETER_HEADER modeData = NULL ;
PMODE_CONTROL_PAGE pageData = NULL ;
ULONG size = 0 ;
modeData = ExAllocatePoolWithTag ( NonPagedPoolNxCacheAligned ,
MODE_PAGE_DATA_SIZE ,
CLASS_TAG_MODE_DATA ) ;
if ( modeData = = NULL ) {
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_SCSI , " ClasspZeroQERR: Unable to allocate mode data buffer \n " ) ) ;
goto ClasspZeroQERR_Exit ;
}
RtlZeroMemory ( modeData , MODE_PAGE_DATA_SIZE ) ;
size = ClassModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CONTROL ) ;
if ( size < sizeof ( MODE_PARAMETER_HEADER ) ) {
//
// Retry the request in case of a check condition.
//
size = ClassModeSense ( DeviceObject ,
( PCHAR ) modeData ,
MODE_PAGE_DATA_SIZE ,
MODE_PAGE_CONTROL ) ;
if ( size < sizeof ( MODE_PARAMETER_HEADER ) ) {
TracePrint ( ( TRACE_LEVEL_WARNING , TRACE_FLAG_SCSI , " ClasspZeroQERR: Mode Sense failed \n " ) ) ;
goto ClasspZeroQERR_Exit ;
}
}
//
// If the size is greater than size indicated by the mode data reset
// the data to the mode data.
//
if ( size > ( ULONG ) ( modeData - > ModeDataLength + 1 ) ) {
size = modeData - > ModeDataLength + 1 ;
}
//
// Look for control page in the returned mode page data.
//
pageData = ClassFindModePage ( ( PCHAR ) modeData ,
size ,
MODE_PAGE_CONTROL ,
TRUE ) ;
if ( pageData ) {
TracePrint ( ( TRACE_LEVEL_VERBOSE ,
TRACE_FLAG_SCSI ,
" ClasspZeroQERR (%p): Current settings: QERR = %u, TST = %u, TAS = %u. \n " ,
DeviceObject ,
pageData - > QERR ,
pageData - > TST ,
pageData - > TAS ) ) ;
if ( pageData - > QERR ! = 0 ) {
NTSTATUS status ;
UCHAR pageSavable = 0 ;
//
// Set QERR to 0 with a Mode Select command. Re-use the modeData
// and pageData structures.
//
pageData - > QERR = 0 ;
//
// We use the original Page Savable (PS) value for the Save Pages
// (SP) bit due to behavior described under the MODE SELECT(6)
// section of SPC-4.
//
pageSavable = pageData - > PageSavable ;
status = ClasspModeSelect ( DeviceObject ,
( PCHAR ) modeData ,
size ,
pageSavable ) ;
if ( ! NT_SUCCESS ( status ) ) {
TracePrint ( ( TRACE_LEVEL_WARNING ,
TRACE_FLAG_SCSI ,
" ClasspZeroQERR (%p): Failed to set QERR = 0 with status %x \n " ,
DeviceObject ,
status ) ) ;
}
}
}
ClasspZeroQERR_Exit :
if ( modeData ! = NULL ) {
ExFreePool ( modeData ) ;
}
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
NTSTATUS
ClasspPowerActivateDevice (
_In_ PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description :
This routine synchronously sends an IOCTL_STORAGE_POWER_ACTIVE to the port
PDO in order to take an active reference on the given device . The device
will remain powered up and active for as long as this active reference is
taken .
The caller should ensure idle power management is enabled for the device
before calling this function .
Call ClasspPowerIdleDevice to release the active reference .
Arguments :
DeviceObject - Supplies the FDO associated with this request .
Return Value :
STATUS_SUCCESS if the active reference was successfully taken .
- - */
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PIRP irp ;
KEVENT event ;
IO_STATUS_BLOCK ioStatus ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
NT_ASSERT ( fdoExtension - > CommonExtension . IsFdo ) ;
NT_ASSERT ( fdoExtension - > FunctionSupportInfo - > IdlePower . IdlePowerEnabled ) ;
KeInitializeEvent ( & event , SynchronizationEvent , FALSE ) ;
irp = IoBuildDeviceIoControlRequest ( IOCTL_STORAGE_POWER_ACTIVE ,
fdoExtension - > LowerPdo ,
NULL ,
0 ,
NULL ,
0 ,
FALSE ,
& event ,
& ioStatus ) ;
if ( irp ! = NULL ) {
status = IoCallDriver ( fdoExtension - > LowerPdo , irp ) ;
if ( status = = STATUS_PENDING ) {
( VOID ) KeWaitForSingleObject ( & event , Executive , KernelMode , FALSE , NULL ) ;
status = ioStatus . Status ;
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES ;
}
return status ;
}
_IRQL_requires_max_ ( PASSIVE_LEVEL )
NTSTATUS
ClasspPowerIdleDevice (
_In_ PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description :
This routine synchronously sends an IOCTL_STORAGE_POWER_IDLE to the port
PDO in order to release an active reference on the given device .
A call to ClasspPowerActivateDevice * must * have preceded a call to this
function .
The caller should ensure idle power management is enabled for the device
before calling this function .
Arguments :
DeviceObject - Supplies the FDO associated with this request .
Return Value :
STATUS_SUCCESS if the active reference was successfully released .
- - */
{
NTSTATUS status = STATUS_UNSUCCESSFUL ;
PIRP irp ;
KEVENT event ;
IO_STATUS_BLOCK ioStatus ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = ( PFUNCTIONAL_DEVICE_EXTENSION ) DeviceObject - > DeviceExtension ;
NT_ASSERT ( fdoExtension - > CommonExtension . IsFdo ) ;
NT_ASSERT ( fdoExtension - > FunctionSupportInfo - > IdlePower . IdlePowerEnabled ) ;
KeInitializeEvent ( & event , SynchronizationEvent , FALSE ) ;
irp = IoBuildDeviceIoControlRequest ( IOCTL_STORAGE_POWER_IDLE ,
fdoExtension - > LowerPdo ,
NULL ,
0 ,
NULL ,
0 ,
FALSE ,
& event ,
& ioStatus ) ;
if ( irp ! = NULL ) {
status = IoCallDriver ( fdoExtension - > LowerPdo , irp ) ;
if ( status = = STATUS_PENDING ) {
( VOID ) KeWaitForSingleObject ( & event , Executive , KernelMode , FALSE , NULL ) ;
status = ioStatus . Status ;
}
} else {
status = STATUS_INSUFFICIENT_RESOURCES ;
}
return status ;
}
# if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
NTSTATUS
ClasspGetHwFirmwareInfo (
_In_ PDEVICE_OBJECT DeviceObject
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PSTORAGE_HW_FIRMWARE_INFO firmwareInfo = NULL ;
PSTORAGE_HW_FIRMWARE_INFO_QUERY query = NULL ;
IO_STATUS_BLOCK ioStatus = { 0 } ;
ULONG dataLength = sizeof ( STORAGE_HW_FIRMWARE_INFO ) ;
ULONG iteration = 1 ;
CLASS_FUNCTION_SUPPORT oldState ;
KLOCK_QUEUE_HANDLE lockHandle ;
//
// Try to get firmware information that contains only one slot.
// We will retry the query if the required buffer size is bigger than that.
//
retry :
firmwareInfo = ExAllocatePoolWithTag ( NonPagedPoolNx , dataLength , CLASSPNP_POOL_TAG_FIRMWARE ) ;
if ( firmwareInfo = = NULL ) {
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_INIT , " ClasspGetHwFirmwareInfo: cannot allocate memory to hold data. \n " ) ) ;
return STATUS_INSUFFICIENT_RESOURCES ;
}
RtlZeroMemory ( firmwareInfo , dataLength ) ;
//
// Set up query data, making sure the "Flags" field indicating the request is for device itself.
//
query = ( PSTORAGE_HW_FIRMWARE_INFO_QUERY ) firmwareInfo ;
query - > Version = sizeof ( STORAGE_HW_FIRMWARE_INFO_QUERY ) ;
query - > Size = sizeof ( STORAGE_HW_FIRMWARE_INFO_QUERY ) ;
query - > Flags = 0 ;
//
// On the first pass we just want to get the first few
// bytes of the descriptor so we can read it's size
//
ClassSendDeviceIoControlSynchronous ( IOCTL_STORAGE_FIRMWARE_GET_INFO ,
commonExtension - > LowerDeviceObject ,
query ,
sizeof ( STORAGE_HW_FIRMWARE_INFO_QUERY ) ,
dataLength ,
FALSE ,
& ioStatus
) ;
if ( ! NT_SUCCESS ( ioStatus . Status ) & &
( ioStatus . Status ! = STATUS_BUFFER_OVERFLOW ) ) {
if ( ClasspLowerLayerNotSupport ( ioStatus . Status ) ) {
oldState = InterlockedCompareExchange ( ( PLONG ) ( & fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport ) , ( LONG ) NotSupported , ( ULONG ) SupportUnknown ) ;
}
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_INIT , " ClasspGetHwFirmwareInfo: error %lx trying to "
" query hardware firmware information #%d \n " , ioStatus . Status , iteration ) ) ;
FREE_POOL ( firmwareInfo ) ;
return ioStatus . Status ;
}
//
// Catch implementation issues from lower level driver.
//
if ( ( firmwareInfo - > Version < sizeof ( STORAGE_HW_FIRMWARE_INFO ) ) | |
( firmwareInfo - > Size < sizeof ( STORAGE_HW_FIRMWARE_INFO ) ) | |
( firmwareInfo - > SlotCount = = 0 ) | |
( firmwareInfo - > ImagePayloadMaxSize > fdoExtension - > AdapterDescriptor - > MaximumTransferLength ) ) {
oldState = InterlockedCompareExchange ( ( PLONG ) ( & fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport ) , ( LONG ) NotSupported , ( ULONG ) SupportUnknown ) ;
TracePrint ( ( TRACE_LEVEL_ERROR , TRACE_FLAG_INIT , " ClasspGetHwFirmwareInfo: error in returned data! "
" Version: 0x%X, Size: 0x%X, SlotCount: 0x%X, ActiveSlot: 0x%X, PendingActiveSlot: 0x%X, ImagePayloadMaxSize: 0x%X \n " ,
firmwareInfo - > Version ,
firmwareInfo - > Size ,
firmwareInfo - > SlotCount ,
firmwareInfo - > ActiveSlot ,
firmwareInfo - > PendingActivateSlot ,
firmwareInfo - > ImagePayloadMaxSize ) ) ;
FREE_POOL ( firmwareInfo ) ;
return STATUS_UNSUCCESSFUL ;
}
//
// If the data size is bigger than sizeof(STORAGE_HW_FIRMWARE_INFO), e.g. device has more than one firmware slot,
// allocate a buffer to get all the data.
//
if ( ( firmwareInfo - > Size > sizeof ( STORAGE_HW_FIRMWARE_INFO ) ) & &
( iteration < 2 ) ) {
dataLength = max ( firmwareInfo - > Size , sizeof ( STORAGE_HW_FIRMWARE_INFO ) + sizeof ( STORAGE_HW_FIRMWARE_SLOT_INFO ) * ( firmwareInfo - > SlotCount - 1 ) ) ;
//
// Retry the query with required buffer length.
//
FREE_POOL ( firmwareInfo ) ;
iteration + + ;
goto retry ;
}
//
// Set the support status and use the memory we've allocated as caching buffer.
// In case of a competing thread already set the state, it will assign the caching buffer so release the current allocated one.
//
KeAcquireInStackQueuedSpinLock ( & fdoExtension - > FunctionSupportInfo - > SyncLock , & lockHandle ) ;
oldState = InterlockedCompareExchange ( ( PLONG ) ( & fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport ) , ( LONG ) Supported , ( ULONG ) SupportUnknown ) ;
if ( oldState = = SupportUnknown ) {
fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = firmwareInfo ;
} else if ( oldState = = Supported ) {
//
// swap the buffers to keep the latest version.
//
PSTORAGE_HW_FIRMWARE_INFO cachedInfo = fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo ;
fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = firmwareInfo ;
FREE_POOL ( cachedInfo ) ;
} else {
FREE_POOL ( firmwareInfo ) ;
}
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
return ioStatus . Status ;
} // end ClasspGetHwFirmwareInfo()
# endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
2020-12-05 17:52:13 +00:00
# ifndef __REACTOS__ // the functions is not used
2020-08-29 03:06:22 +00:00
__inline
BOOLEAN
ClassDeviceHwFirmwareIsPortDriverSupported (
_In_ PDEVICE_OBJECT DeviceObject
)
/*
Routine Description :
This function informs the caller whether the port driver supports hardware firmware requests .
Arguments :
DeviceObject : The target object .
Return Value :
TRUE if the port driver is supported .
- - */
{
//
// If the request is for a FDO, process the request for Storport, SDstor and Spaceport only.
// Don't process it if we don't have a miniport descriptor.
//
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
BOOLEAN isSupported = FALSE ;
if ( commonExtension - > IsFdo & & ( fdoExtension - > MiniportDescriptor ! = NULL ) ) {
isSupported = ( ( fdoExtension - > MiniportDescriptor - > Portdriver = = StoragePortCodeSetStorport ) | |
( fdoExtension - > MiniportDescriptor - > Portdriver = = StoragePortCodeSetSpaceport ) | |
( fdoExtension - > MiniportDescriptor - > Portdriver = = StoragePortCodeSetSDport ) ) ;
}
return isSupported ;
}
2020-12-05 17:52:13 +00:00
# endif
2020-08-29 03:06:22 +00:00
NTSTATUS
ClassDeviceHwFirmwareGetInfoProcess (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp
)
/*
Routine Description :
This function processes the Storage Hardware Firmware Get Information request .
If the information is not cached yet , it gets from lower level driver .
Arguments :
DeviceObject : The target FDO .
Irp : The IRP which will contain the output buffer upon completion .
Return Value :
NTSTATUS code .
- - */
{
NTSTATUS status = STATUS_SUCCESS ;
# if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_HW_FIRMWARE_INFO_QUERY query = ( PSTORAGE_HW_FIRMWARE_INFO_QUERY ) Irp - > AssociatedIrp . SystemBuffer ;
BOOLEAN passDown = FALSE ;
BOOLEAN copyData = FALSE ;
//
// Input buffer is not big enough to contain required input information.
//
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength < sizeof ( STORAGE_HW_FIRMWARE_INFO_QUERY ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
goto Exit_Firmware_Get_Info ;
}
//
// Output buffer is too small to contain return data.
//
if ( irpStack - > Parameters . DeviceIoControl . OutputBufferLength < sizeof ( STORAGE_HW_FIRMWARE_INFO ) ) {
status = STATUS_BUFFER_TOO_SMALL ;
goto Exit_Firmware_Get_Info ;
}
//
// Only process the request for a supported port driver.
//
if ( ! ClassDeviceHwFirmwareIsPortDriverSupported ( DeviceObject ) ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Get_Info ;
}
//
// Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
// of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
//
if ( commonExtension - > IsFdo & & ( fdoExtension - > FunctionSupportInfo = = NULL ) ) {
status = STATUS_UNSUCCESSFUL ;
goto Exit_Firmware_Get_Info ;
}
//
// Process the situation that request should be forwarded to lower level.
//
if ( ! commonExtension - > IsFdo ) {
passDown = TRUE ;
}
if ( ( query - > Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER ) ! = 0 ) {
passDown = TRUE ;
}
if ( passDown ) {
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
return status ;
}
//
// The request is for a FDO. Process the request.
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport = = NotSupported ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Get_Info ;
} else {
//
// Retrieve information from lower layer for the request. The cached information is not used
// in case device firmware information changed.
//
status = ClasspGetHwFirmwareInfo ( DeviceObject ) ;
copyData = NT_SUCCESS ( status ) ;
}
Exit_Firmware_Get_Info :
if ( copyData ) {
//
// Firmware information is already cached in classpnp. Return a copy.
//
KLOCK_QUEUE_HANDLE lockHandle ;
KeAcquireInStackQueuedSpinLock ( & fdoExtension - > FunctionSupportInfo - > SyncLock , & lockHandle ) ;
ULONG dataLength = min ( irpStack - > Parameters . DeviceIoControl . OutputBufferLength , fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > Size ) ;
memcpy ( Irp - > AssociatedIrp . SystemBuffer , fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo , dataLength ) ;
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
Irp - > IoStatus . Information = dataLength ;
}
Irp - > IoStatus . Status = status ;
# else
status = STATUS_NOT_IMPLEMENTED ;
Irp - > IoStatus . Status = status ;
# endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
return status ;
}
# if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
_IRQL_requires_same_
_IRQL_requires_max_ ( DISPATCH_LEVEL )
NTSTATUS
ClassHwFirmwareDownloadComplete (
_In_ PDEVICE_OBJECT Fdo ,
_In_ PIRP Irp ,
_In_reads_opt_ ( _Inexpressible_ ( " varies " ) ) PVOID Context
)
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PIRP originalIrp ;
//
// Free the allocated buffer for firmware image.
//
if ( Context ! = NULL ) {
FREE_POOL ( Context ) ;
}
originalIrp = irpStack - > Parameters . Others . Argument1 ;
NT_ASSERT ( originalIrp ! = NULL ) ;
originalIrp - > IoStatus . Status = Irp - > IoStatus . Status ;
originalIrp - > IoStatus . Information = Irp - > IoStatus . Information ;
ClassReleaseRemoveLock ( Fdo , originalIrp ) ;
ClassCompleteRequest ( Fdo , originalIrp , IO_DISK_INCREMENT ) ;
IoFreeIrp ( Irp ) ;
return STATUS_MORE_PROCESSING_REQUIRED ;
} // end ClassHwFirmwareDownloadComplete()
# endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
NTSTATUS
ClassDeviceHwFirmwareDownloadProcess (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
{
NTSTATUS status = STATUS_SUCCESS ;
# if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_HW_FIRMWARE_DOWNLOAD firmwareDownload = ( PSTORAGE_HW_FIRMWARE_DOWNLOAD ) Irp - > AssociatedIrp . SystemBuffer ;
BOOLEAN passDown = FALSE ;
ULONG i ;
ULONG bufferSize = 0 ;
PUCHAR firmwareImageBuffer = NULL ;
PIRP irp2 = NULL ;
PIO_STACK_LOCATION newStack = NULL ;
PCDB cdb = NULL ;
BOOLEAN lockHeld = FALSE ;
KLOCK_QUEUE_HANDLE lockHandle ;
//
// Input buffer is not big enough to contain required input information.
//
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength < sizeof ( STORAGE_HW_FIRMWARE_DOWNLOAD ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
goto Exit_Firmware_Download ;
}
//
// Input buffer basic validation.
//
if ( ( firmwareDownload - > Version < sizeof ( STORAGE_HW_FIRMWARE_DOWNLOAD ) ) | |
( firmwareDownload - > Size > irpStack - > Parameters . DeviceIoControl . InputBufferLength ) | |
( ( firmwareDownload - > BufferSize + FIELD_OFFSET ( STORAGE_HW_FIRMWARE_DOWNLOAD , ImageBuffer ) ) > firmwareDownload - > Size ) ) {
status = STATUS_INVALID_PARAMETER ;
goto Exit_Firmware_Download ;
}
//
// Only process the request for a supported port driver.
//
if ( ! ClassDeviceHwFirmwareIsPortDriverSupported ( DeviceObject ) ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Download ;
}
//
// Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
// of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
//
if ( commonExtension - > IsFdo & & ( fdoExtension - > FunctionSupportInfo = = NULL ) ) {
status = STATUS_UNSUCCESSFUL ;
goto Exit_Firmware_Download ;
}
//
// Process the situation that request should be forwarded to lower level.
//
if ( ! commonExtension - > IsFdo ) {
passDown = TRUE ;
}
if ( ( firmwareDownload - > Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER ) ! = 0 ) {
passDown = TRUE ;
}
if ( passDown ) {
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
FREE_POOL ( Srb ) ;
return status ;
}
//
// If firmware information hasn't been cached in classpnp, retrieve it.
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = = NULL ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport = = NotSupported ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Download ;
} else {
//
// If this is the first time of retrieving firmware information,
// send request to lower level to get it.
//
status = ClasspGetHwFirmwareInfo ( DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) ) {
goto Exit_Firmware_Download ;
}
}
}
//
// Fail the request if the firmware information cannot be retrieved.
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = = NULL ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport = = NotSupported ) {
status = STATUS_NOT_IMPLEMENTED ;
} else {
status = STATUS_UNSUCCESSFUL ;
}
goto Exit_Firmware_Download ;
}
//
// Acquire the SyncLock to ensure the HwFirmwareInfo pointer doesn't change
// while we're dereferencing it.
//
lockHeld = TRUE ;
KeAcquireInStackQueuedSpinLock ( & fdoExtension - > FunctionSupportInfo - > SyncLock , & lockHandle ) ;
//
// Validate the device support
//
if ( ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SupportUpgrade = = FALSE ) | |
( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadAlignment = = 0 ) ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit_Firmware_Download ;
}
//
// Check if the slot can be used to hold firmware image.
//
for ( i = 0 ; i < fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SlotCount ; i + + ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > Slot [ i ] . SlotNumber = = firmwareDownload - > Slot ) {
break ;
}
}
if ( ( i > = fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SlotCount ) | |
( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > Slot [ i ] . ReadOnly = = TRUE ) ) {
//
// Either the slot number is out of scope or the slot is read-only.
//
status = STATUS_INVALID_PARAMETER ;
goto Exit_Firmware_Download ;
}
//
// Buffer size and alignment validation.
// Max Offset and Buffer Size can be represented by SCSI command is max value for 3 bytes.
//
if ( ( firmwareDownload - > BufferSize = = 0 ) | |
( ( firmwareDownload - > BufferSize % fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadAlignment ) ! = 0 ) | |
( firmwareDownload - > BufferSize > fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadMaxSize ) | |
( firmwareDownload - > BufferSize > fdoExtension - > AdapterDescriptor - > MaximumTransferLength ) | |
( ( firmwareDownload - > Offset % fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadAlignment ) ! = 0 ) | |
( firmwareDownload - > Offset > 0xFFFFFF ) | |
( firmwareDownload - > BufferSize > 0xFFFFFF ) ) {
status = STATUS_INVALID_PARAMETER ;
goto Exit_Firmware_Download ;
}
//
// Process the request by translating it into WRITE BUFFER command.
//
if ( ( ( ULONG_PTR ) firmwareDownload - > ImageBuffer % fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadAlignment ) ! = 0 ) {
//
// Allocate buffer aligns to ImagePayloadAlignment to accommodate the alignment requirement.
//
bufferSize = ALIGN_UP_BY ( firmwareDownload - > BufferSize , fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > ImagePayloadAlignment ) ;
//
// We're done accessing HwFirmwareInfo at this point so we can release
// the SyncLock.
//
NT_ASSERT ( lockHeld ) ;
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
lockHeld = FALSE ;
# ifdef _MSC_VER
# pragma prefast(suppress:6014, "The allocated memory that firmwareImageBuffer points to will be freed in ClassHwFirmwareDownloadComplete().")
# endif
firmwareImageBuffer = ExAllocatePoolWithTag ( NonPagedPoolNx , bufferSize , CLASSPNP_POOL_TAG_FIRMWARE ) ;
if ( firmwareImageBuffer = = NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES ;
goto Exit_Firmware_Download ;
}
RtlZeroMemory ( firmwareImageBuffer , bufferSize ) ;
RtlCopyMemory ( firmwareImageBuffer , firmwareDownload - > ImageBuffer , ( ULONG ) firmwareDownload - > BufferSize ) ;
} else {
NT_ASSERT ( lockHeld ) ;
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
lockHeld = FALSE ;
firmwareImageBuffer = firmwareDownload - > ImageBuffer ;
bufferSize = ( ULONG ) firmwareDownload - > BufferSize ;
}
//
// Allocate a new irp to send the WRITE BUFFER command down.
// Similar process as IOCTL_STORAGE_CHECK_VERIFY.
//
irp2 = IoAllocateIrp ( ( CCHAR ) ( DeviceObject - > StackSize + 3 ) , FALSE ) ;
if ( irp2 = = NULL ) {
status = STATUS_INSUFFICIENT_RESOURCES ;
if ( firmwareImageBuffer ! = firmwareDownload - > ImageBuffer ) {
FREE_POOL ( firmwareImageBuffer ) ;
}
goto Exit_Firmware_Download ;
}
//
// Make sure to acquire the lock for the new irp.
//
ClassAcquireRemoveLock ( DeviceObject , irp2 ) ;
irp2 - > Tail . Overlay . Thread = Irp - > Tail . Overlay . Thread ;
IoSetNextIrpStackLocation ( irp2 ) ;
//
// Set the top stack location and shove the master Irp into the
// top location
//
newStack = IoGetCurrentIrpStackLocation ( irp2 ) ;
newStack - > Parameters . Others . Argument1 = Irp ;
newStack - > DeviceObject = DeviceObject ;
//
// Stick the firmware download completion routine onto the stack
// and prepare the irp for the port driver
//
IoSetCompletionRoutine ( irp2 ,
ClassHwFirmwareDownloadComplete ,
( firmwareImageBuffer ! = firmwareDownload - > ImageBuffer ) ? firmwareImageBuffer : NULL ,
TRUE ,
TRUE ,
TRUE ) ;
IoSetNextIrpStackLocation ( irp2 ) ;
newStack = IoGetCurrentIrpStackLocation ( irp2 ) ;
newStack - > DeviceObject = DeviceObject ;
newStack - > MajorFunction = irpStack - > MajorFunction ;
newStack - > MinorFunction = irpStack - > MinorFunction ;
newStack - > Flags = irpStack - > Flags ;
//
// Mark the master irp as pending - whether the lower level
// driver completes it immediately or not this should allow it
// to go all the way back up.
//
IoMarkIrpPending ( Irp ) ;
//
// Setup the CDB.
//
SrbSetCdbLength ( Srb , CDB10GENERIC_LENGTH ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > WRITE_BUFFER . OperationCode = SCSIOP_WRITE_DATA_BUFF ;
cdb - > WRITE_BUFFER . Mode = SCSI_WRITE_BUFFER_MODE_DOWNLOAD_MICROCODE_WITH_OFFSETS_SAVE_DEFER_ACTIVATE ;
cdb - > WRITE_BUFFER . ModeSpecific = 0 ; //Reserved for Mode 0x0E
cdb - > WRITE_BUFFER . BufferID = firmwareDownload - > Slot ;
cdb - > WRITE_BUFFER . BufferOffset [ 0 ] = * ( ( PCHAR ) & firmwareDownload - > Offset + 2 ) ;
cdb - > WRITE_BUFFER . BufferOffset [ 1 ] = * ( ( PCHAR ) & firmwareDownload - > Offset + 1 ) ;
cdb - > WRITE_BUFFER . BufferOffset [ 2 ] = * ( ( PCHAR ) & firmwareDownload - > Offset ) ;
cdb - > WRITE_BUFFER . ParameterListLength [ 0 ] = * ( ( PCHAR ) & bufferSize + 2 ) ;
cdb - > WRITE_BUFFER . ParameterListLength [ 1 ] = * ( ( PCHAR ) & bufferSize + 1 ) ;
cdb - > WRITE_BUFFER . ParameterListLength [ 2 ] = * ( ( PCHAR ) & bufferSize ) ;
//
// Send as a tagged command.
//
SrbSetRequestAttribute ( Srb , SRB_HEAD_OF_QUEUE_TAG_REQUEST ) ;
SrbSetSrbFlags ( Srb , SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE ) ;
//
// Set timeout value.
//
SrbSetTimeOutValue ( Srb , fdoExtension - > TimeOutValue ) ;
//
// This routine uses a completion routine so we don't want to release
// the remove lock until then.
//
status = ClassSendSrbAsynchronous ( DeviceObject ,
Srb ,
irp2 ,
firmwareImageBuffer ,
bufferSize ,
TRUE ) ;
if ( status ! = STATUS_PENDING ) {
//
// If the new irp cannot be sent down, free allocated memory and bail out.
//
if ( firmwareImageBuffer ! = firmwareDownload - > ImageBuffer ) {
FREE_POOL ( firmwareImageBuffer ) ;
}
//
// If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
//
Srb = NULL ;
ClassReleaseRemoveLock ( DeviceObject , irp2 ) ;
IoFreeIrp ( irp2 ) ;
goto Exit_Firmware_Download ;
}
return status ;
Exit_Firmware_Download :
//
// Release the SyncLock if it's still held.
// This should only happen in the failure path.
//
if ( lockHeld ) {
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
lockHeld = FALSE ;
}
//
// Firmware Download request will be failed.
//
NT_ASSERT ( ! NT_SUCCESS ( status ) ) ;
Irp - > IoStatus . Status = status ;
# else
status = STATUS_NOT_IMPLEMENTED ;
Irp - > IoStatus . Status = status ;
# endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
FREE_POOL ( Srb ) ;
return status ;
}
NTSTATUS
ClassDeviceHwFirmwareActivateProcess (
_In_ PDEVICE_OBJECT DeviceObject ,
_Inout_ PIRP Irp ,
_Inout_ PSCSI_REQUEST_BLOCK Srb
)
{
NTSTATUS status = STATUS_SUCCESS ;
# if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject - > DeviceExtension ;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject - > DeviceExtension ;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation ( Irp ) ;
PSTORAGE_HW_FIRMWARE_ACTIVATE firmwareActivate = ( PSTORAGE_HW_FIRMWARE_ACTIVATE ) Irp - > AssociatedIrp . SystemBuffer ;
BOOLEAN passDown = FALSE ;
PCDB cdb = NULL ;
ULONG i ;
BOOLEAN lockHeld = FALSE ;
KLOCK_QUEUE_HANDLE lockHandle ;
//
// Input buffer is not big enough to contain required input information.
//
if ( irpStack - > Parameters . DeviceIoControl . InputBufferLength < sizeof ( STORAGE_HW_FIRMWARE_ACTIVATE ) ) {
status = STATUS_INFO_LENGTH_MISMATCH ;
goto Exit_Firmware_Activate ;
}
//
// Input buffer basic validation.
//
if ( ( firmwareActivate - > Version < sizeof ( STORAGE_HW_FIRMWARE_ACTIVATE ) ) | |
( firmwareActivate - > Size > irpStack - > Parameters . DeviceIoControl . InputBufferLength ) ) {
status = STATUS_INVALID_PARAMETER ;
goto Exit_Firmware_Activate ;
}
//
// Only process the request for a supported port driver.
//
if ( ! ClassDeviceHwFirmwareIsPortDriverSupported ( DeviceObject ) ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Activate ;
}
//
// Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation
// of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet.
//
if ( commonExtension - > IsFdo & & ( fdoExtension - > FunctionSupportInfo = = NULL ) ) {
status = STATUS_UNSUCCESSFUL ;
goto Exit_Firmware_Activate ;
}
//
// Process the situation that request should be forwarded to lower level.
//
if ( ! commonExtension - > IsFdo ) {
passDown = TRUE ;
}
if ( ( firmwareActivate - > Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER ) ! = 0 ) {
passDown = TRUE ;
}
if ( passDown ) {
IoCopyCurrentIrpStackLocationToNext ( Irp ) ;
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
status = IoCallDriver ( commonExtension - > LowerDeviceObject , Irp ) ;
FREE_POOL ( Srb ) ;
return status ;
}
//
// If firmware information hasn't been cached in classpnp, retrieve it.
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = = NULL ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport = = NotSupported ) {
status = STATUS_NOT_IMPLEMENTED ;
goto Exit_Firmware_Activate ;
} else {
//
// If this is the first time of retrieving firmware information,
// send request to lower level to get it.
//
status = ClasspGetHwFirmwareInfo ( DeviceObject ) ;
if ( ! NT_SUCCESS ( status ) ) {
goto Exit_Firmware_Activate ;
}
}
}
//
// Fail the request if the firmware information cannot be retrieved.
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo = = NULL ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareGetInfoSupport = = NotSupported ) {
status = STATUS_NOT_IMPLEMENTED ;
} else {
status = STATUS_UNSUCCESSFUL ;
}
goto Exit_Firmware_Activate ;
}
//
// Acquire the SyncLock to ensure the HwFirmwareInfo pointer doesn't change
// while we're dereferencing it.
//
lockHeld = TRUE ;
KeAcquireInStackQueuedSpinLock ( & fdoExtension - > FunctionSupportInfo - > SyncLock , & lockHandle ) ;
//
// Validate the device support
//
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SupportUpgrade = = FALSE ) {
status = STATUS_NOT_SUPPORTED ;
goto Exit_Firmware_Activate ;
}
//
// Check if the slot number is valid.
//
for ( i = 0 ; i < fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SlotCount ; i + + ) {
if ( fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > Slot [ i ] . SlotNumber = = firmwareActivate - > Slot ) {
break ;
}
}
if ( i > = fdoExtension - > FunctionSupportInfo - > HwFirmwareInfo - > SlotCount ) {
//
// Either the slot number is out of scope or the slot is read-only.
//
status = STATUS_INVALID_PARAMETER ;
goto Exit_Firmware_Activate ;
}
//
// We're done accessing HwFirmwareInfo at this point so we can release
// the SyncLock.
//
NT_ASSERT ( lockHeld ) ;
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
lockHeld = FALSE ;
//
// Process the request by translating it into WRITE BUFFER command.
//
//
// Setup the CDB. This should be an untagged request.
//
SrbSetCdbLength ( Srb , CDB10GENERIC_LENGTH ) ;
cdb = SrbGetCdb ( Srb ) ;
cdb - > WRITE_BUFFER . OperationCode = SCSIOP_WRITE_DATA_BUFF ;
cdb - > WRITE_BUFFER . Mode = SCSI_WRITE_BUFFER_MODE_ACTIVATE_DEFERRED_MICROCODE ;
cdb - > WRITE_BUFFER . ModeSpecific = 0 ; //Reserved for Mode 0x0F
cdb - > WRITE_BUFFER . BufferID = firmwareActivate - > Slot ; //NOTE: this field will be ignored by SCSI device.
//
// Set timeout value.
//
SrbSetTimeOutValue ( Srb , FIRMWARE_ACTIVATE_TIMEOUT_VALUE ) ;
//
// This routine uses a completion routine - ClassIoComplete() so we don't want to release
// the remove lock until then.
//
status = ClassSendSrbAsynchronous ( DeviceObject ,
Srb ,
Irp ,
NULL ,
0 ,
FALSE ) ;
if ( status ! = STATUS_PENDING ) {
//
// If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
//
Srb = NULL ;
goto Exit_Firmware_Activate ;
}
return status ;
Exit_Firmware_Activate :
//
// Release the SyncLock if it's still held.
// This should only happen in the failure path.
//
if ( lockHeld ) {
KeReleaseInStackQueuedSpinLock ( & lockHandle ) ;
lockHeld = FALSE ;
}
//
// Firmware Activate request will be failed.
//
NT_ASSERT ( ! NT_SUCCESS ( status ) ) ;
Irp - > IoStatus . Status = status ;
# else
status = STATUS_NOT_IMPLEMENTED ;
Irp - > IoStatus . Status = status ;
# endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
ClassReleaseRemoveLock ( DeviceObject , Irp ) ;
ClassCompleteRequest ( DeviceObject , Irp , IO_NO_INCREMENT ) ;
FREE_POOL ( Srb ) ;
return status ;
}
BOOLEAN
ClasspIsThinProvisioningError (
_In_ PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description :
This routine checks whether the completed SRB Srb was completed with a thin provisioning
soft threshold error .
Arguments :
Srb - the SRB to be checked .
Return Value :
BOOLEAN
- - */
{
if ( TEST_FLAG ( Srb - > SrbStatus , SRB_STATUS_AUTOSENSE_VALID ) ) {
PVOID senseBuffer = SrbGetSenseInfoBuffer ( Srb ) ;
if ( senseBuffer ) {
UCHAR senseKey = 0 ;
UCHAR addlSenseCode = 0 ;
UCHAR addlSenseCodeQual = 0 ;
BOOLEAN validSense = ScsiGetSenseKeyAndCodes ( senseBuffer ,
SrbGetSenseInfoBufferLength ( Srb ) ,
SCSI_SENSE_OPTIONS_NONE ,
& senseKey ,
& addlSenseCode ,
& addlSenseCodeQual ) ;
return ( validSense
& & ( senseKey = = SCSI_SENSE_UNIT_ATTENTION )
& & ( addlSenseCode = = SCSI_ADSENSE_LB_PROVISIONING )
& & ( addlSenseCodeQual = = SCSI_SENSEQ_SOFT_THRESHOLD_REACHED ) ) ;
}
}
return FALSE ;
}