Topic Overview
------------------

1.) Why SMART commands do not work in combination with storport
2.) How the SMART commands from CrystalDiskInfo version 8.11.2 traverse down the device stack
3.) Why START STOP UNIT command is not send to HDD / SSD drives
4.) How to fix the logon screen instant reboot bug if a CD-/DVD-ROM drive is connected
5.) Make storport from Windows 2003 x86 no SP leaked source code work with StorAhci driver
6.) How to compile our changed storport driver with SMART support based on leaked sources
7.) Patch original Windows 7 SP1 x86 storport to make it work with Windows 2003 Server
8.) How to apply the storport patches
9.) Credits and Download Links


1.) Why SMART commands do not work in combination with storport
------------------------------------------------------------------------

S.M.A.R.T. is short for "Self-Monitoring, Analysis and Reporting Technology" which is a monitoring system included in hard disk drives (HDDs) and solid-state drives (SSDs). In general SMART commands are ATA commands, therefore a SCSI device and it's driver does not have to support this command natively, although almost nearly all drives do. Since the introduction of storport in Windows 2003 Server the command was not send to the drive at all. The first major change was done starting with Windows 8, where the SMART command was forwarded from storport to the manufacturer's driver. The storport driver of Windows 7 has also the ability to forward this command, but was limited inside the code.

In the following tutorial we show two possible solutions to the problem. The first solution patches the storport driver of the leaked Windows Server 2003 source code archive. The second solution patches storport of Windows 7 and uses it in combination with ntoskrn7.sys, which is sort of a self compiled extender library for ntoskrnl.exe to add support for missing functions called by Windows 7 storport.


2.) How the SMART commands from CrystalDiskInfo version 8.11.2 traverse down the device stack
--------------------------------------------------------------------------------------------------------

CrystalDiskInfo's Advanced Disk Search is able to find the drive at SCSI address 0:0:0 (PathId, TargetId, Lun). We can only find drive 0 this way. All other HDDs can not be found. This is related to the used device handle "\\.\Scsi0:". If we send a command to the connected device by using this handle, the command is only send to the AHCI adapter code of storport. The adapter receives the command in the function RaDriverDeviceControlIrp and calls RaidAdapterScsiMiniportIoctl. In this function the PathId, TargetId and Lun is always set to zero. In the following text we talk about CrystalDiskInfo without Advanced Disk Search. To run CrystalDiskInfo in the so called "normal mode" start the program and go to Menu > Function > Advanced Feature > and make sure that Advanced Disk Search is unchecked.



CrystalDiskInfo sends the I/O control code 0x7C088 DFP_RECEIVE_DRIVE_DATA to read the SMART data of HDD and SSD drives. There are 3 locations in the C++ code where this I/O control code is used. The file AtaSmart.cpp uses this I/O in the following 3 functions:
  • CAtaSmart::DoIdentifyDevicePd
  • → command register ID_CMD 0xEC
  • CAtaSmart::GetSmartAttributePd
  • → command register SMART_CMD 0xB0 and features register READ_ATTRIBUTES 0xD0
  • CAtaSmart::GetSmartThresholdPd
  • → command register SMART_CMD 0xB0 and features register READ_THRESHOLDS 0xD1
The I/O control code DFP_RECEIVE_DRIVE_DATA has the same value as SMART_RCV_DRIVE_DATA, which is defined in ntdddisk.h of the Windows Driver Development Kit. This I/O control code is changed by the driver disk.sys in the source code file disk.c and the function DiskDeviceControl. Based on the values of the command and feature registers the following 3 commands are sent to storport by the function DiskDeviceControl of disk.sys driver:
  • IOCTL_SCSI_MINIPORT_IDENTIFY 0x1B0501
  • → command register ID_CMD 0xEC
  • IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS 0x1B0502
  • → command register SMART_CMD 0xB0 and features register READ_ATTRIBUTES 0xD0
  • IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS 0x1B0503
  • → command register SMART_CMD 0xB0 and features register READ_THRESHOLDS 0xD1
These 3 I/O control codes are used for the ControlCode field of the structure SRB_IO_CONTROL. The function IoBuildDeviceIoControlRequest inside disk.sys uses this SRB_IO_CONTROL structure in combination with the I/O control code IOCTL_SCSI_MINIPORT and sends the commands to storport by calling the function IoCallDriver. The call stack until the command reaches the problematic function RaUnitDeviceControlIrp looks like follows:


Inside storport driver we have a problem in the function RaUnitDeviceControlIrp in the file storport\storage\port\raid\port\unit.c. The I/O control code 0x4D008 IOCTL_SCSI_MINIPORT is missing for Windows Server 2003 R2 SP2. In storport of Server 2008 SP2 the IOCTL code is recognized, but is redirected to RaidAdapterScsiMiniportIoctl, which sets the PathId, TargetId and Lun always to zero.

On storport of Windows 7 with SP1 we see a change here, because RaUnitDeviceControlIrp recognizes the I/O control code IOCTL_SCSI_MINIPORT and calls RaUnitScsiMiniportIoctl. In this function the SRB_IO_CONTROL structure field Signature is checked against the string "HYBRDISK" and the structure field ControlCode is checked against IOCTL_SCSI_MINIPORT_NVCACHE. Only if these two structure fields match, the request is send to the function RaidAdapterScsiMiniportIoctlWithAddress. Any other values of these two structure fields call RaUnitUnknownIoctl and return the NT status value STATUS_INVALID_DEVICE_REQUEST.



On Windows 8 without SP RaUnitDeviceControlIrp recognizes the IOCTL IOCTL_SCSI_MINIPORT and calls RaUnitScsiMiniportIoctl as for Windows 7 with SP1. But on Windows 8 this function then calls the new function RaidAdapterScsiMiniportIoctlWithAddress, no matter what the "Signature" and "ControlCode" fields of the structure SRB_IO_CONTROL contain. The function RaidAdapterScsiMiniportIoctlWithAddress accepts a SCSI PathId (this is equal to the "Bus Number" in device manager), TargetId and Lun for the target device address as arguments. Here the correct device is selected before the request is sent to the HDD drive.

To prove our assumption we changed the leaked source code of Windows 2003 Server storport and rebuild the functions RaUnitScsiMiniportIoctl and RaidAdapterScsiMiniportIoctlWithAddress from Windows 7 SP1. We added a little change and now do not check the "Signature" and "ControlCode" field of the SRB_IO_CONTROL structure. After we use the changed storport driver in combination with our StorAhci driver CrystalDiskInfo shows the SMART values for all connected HDDs and SSDs.


3.) Why START STOP UNIT command is not send to HDD / SSD drives
-------------------------------------------------------------------------

The SCSI START STOP UNIT command does work with storport driver version 5.2.3790.3959 from Windows 2003 Server SP2. To make it also work with the storport from the leaked source code archive, we have to change the following two additional code locations.
if (PowerState.SystemState > PowerSystemShutdown) {
//------------------------------------------------------------------------------
// near 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only trace function WmiTraceMessage is missing
//------------------------------------------------------------------------------
NTSTATUS
RaidUnitSetSystemPowerIrp(
    IN PRAID_UNIT_EXTENSION Unit,
    IN PIRP Irp
    )
{
    NTSTATUS Status;
    POWER_STATE DevicePowerState;
    POWER_STATE SystemPowerState;
	
    DebugPower (("Unit %p, Irp %p, Set System Power, ret SUCCESS\n",
                 Unit, Irp));

    SystemPowerState = RaidPowerStateFromIrp (Irp);
    DevicePowerState.DeviceState = DeviceStateTable [SystemPowerState.SystemState];

    Status = PoRequestPowerIrp (Unit->DeviceObject, IRP_MN_SET_POWER, DevicePowerState, NULL, Irp, NULL);
    if (Status == STATUS_PENDING)
    {
    	Status = STATUS_SUCCESS;	
    }
    PoStartNextPowerIrp (Irp);
    return RaidCompleteRequest (Irp,  Status);
}
After we apply these changes the additional call to PoRequestPowerIrp, which is missing in the source code completely, triggers the storage ClassPnP driver and the function ClasspPowerDownCompletion. This function sends the SYNCHRONIZE_CACHE command opcode 0x35 and after that the START_STOP_UNIT command opcode 0x1B. The START_STOP_UNIT command opcode 0x1B is received by StorAhci in AhciHwStartIo and converted from SCSI to an ATA command. At the end the function AtaStartStopUnitRequest is called, which sends IDE_COMMAND_STANDBY_IMMEDIATE to the drive.


4.) How to fix the logon screen instant reboot bug if a CD-/DVD-ROM drive is connected
---------------------------------------------------------------------------------------------

If a CD-/DVD-ROM drive is connected and the storport driver of the leaked source code is used, we get an instant reboot at the windows logon screen. This is related to a bug in the function RaUnitFlushQueueSrb. In the last call to RaidSrbFromIrp the driver should use FlushIrp, but uses Irp instead. This can throw an exception if Irp is NULL. To fix the bug we changed the function RaUnitFlushQueueSrb.
//------------------------------------------------------------------------------
// 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only the offset of &Unit->IoQueue does not match 100%
//------------------------------------------------------------------------------
NTSTATUS
RaUnitFlushQueueSrb(
    IN PRAID_UNIT_EXTENSION Unit,
    IN PIRP FlushIrp
    )
/*++

Routine Description:

    Flush the pending queue, canceling all requests.

Arguments:

    Unit - Supplies pointer to unit to flush.

    FlushIrp - Supplies the IRP representing the flush queue request.

Return Value:

    NTSTATUS code.

Environment:

    DISPATCH_LEVEL or below.

--*/
{
    PIRP Irp;
    PSCSI_REQUEST_BLOCK Srb;
    LIST_ENTRY listHead;
    PIRP nextIrp = NULL;
    PLIST_ENTRY nextEntry;

    ASSERT (RaidIsUnitQueueFrozen (Unit));

    //
    // Flush all entries.
    //
    
    InitializeListHead (&listHead);
    for (Irp = RaidRemoveIoQueue (&Unit->IoQueue);
         Irp != NULL;
         Irp = RaidRemoveIoQueue (&Unit->IoQueue)) {

        InsertTailList (&listHead,&Irp->Tail.Overlay.ListEntry);
    }

    RaidThawUnitQueue (Unit);

    while (!IsListEmpty (&listHead))
    {
        nextEntry = RemoveHeadList (&listHead);
        nextIrp = CONTAINING_RECORD (nextEntry, IRP, Tail.Overlay.ListEntry);

        Srb = RaidSrbFromIrp (nextIrp);
        Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
        nextIrp->IoStatus.Information = 0;
        RaidCompleteRequest (nextIrp,  STATUS_UNSUCCESSFUL);

        nextIrp = NULL;
    }

    // This solves a bug with CD-/DVD-ROM drives where the original storport
    // driver uses the Irp instead of the FlushIrp here. This throws an
    // exception, because Irp can be NULL.
    Srb = RaidSrbFromIrp (FlushIrp);
    Srb->SrbStatus = SRB_STATUS_SUCCESS;
    FlushIrp->IoStatus.Information = 0;
    return RaidCompleteRequest (FlushIrp,  STATUS_SUCCESS);
}

5.) Make storport from Windows 2003 x86 no SP leaked source code work with StorAhci driver
---------------------------------------------------------------------------------------------------

The reason why StorAhci does not work out of the box with the retail Windows Server 2003 x86 no SP is that there are 5 notification types missing inside storport.sys driver. These notification types are needed to make StorAhci work. Inside the file storage\port\raid\port\port.c we have to add the following notification types to the function StorPortNotification:
  • EnablePassiveInitialization 0x1000
  • → Enable the passive AHCI adapter initialization.
  • InitializeDpc 0x1001
  • → Initialize the deferred procedure call object. This DPC is used inside StorAhci to complete a request for a HDD drive.
  • IssueDpc 0x1002
  • → Call the DPC which was initialized before.
  • AcquireSpinLock 0x1003
  • → Acquire the spin lock associated with the raid adapter interrupt object.
  • ReleaseSpinLock 0x1004
  • → Release the spin lock of the raid adapter.
In addition we have to add the HwPassiveInitRoutine structure member to the _RAID_ADAPTER_EXTENSION in adapter.h and change the function RaidAdapterStartMiniport in adapter.c to call our new passive initialization routine. All the necessary steps are detailed in the next topic.


6.) How to compile our changed storport driver with SMART support based on leaked sources
--------------------------------------------------------------------------------------------------

In the following we do not explain how to get the leaked source code archive nt5src.7z due to legal reasons. You have to do the following steps to build the storport driver with SMART support based on it:
LINKLIBS=$(DDK_LIB_PATH)\hal.lib        \
         $(DDK_LIB_PATH)\ntoskrnl.lib   \
         ..\storlib\$(O)\storlib.lib   \
         ..\..\lib\$(O)\portlib.lib
DLLDEF=raidport.def
  • edit the file C:\storport\storage\port\raid\storlib\debug.c and comment out vDbgPrintExWithPrefix and DbgPrompt
//
// NB: These should come from ntrtl.h
//
/*
ULONG
vDbgPrintExWithPrefix(
    IN PCH Prefix,
    IN ULONG ComponentId,
    IN ULONG Level,
    IN PCSTR Format,
    va_list arglist
    );

NTSYSAPI
ULONG
NTAPI
DbgPrompt(
    PCH Prompt,
    PCH Response,
    ULONG MaximumResponseLength
    );
*/
  • edit the file C:\storport\storage\port\raid\port\adapter.h and add 3 bit fields to the structure _RAID_ADAPTER_EXTENSION by changing it to
        BOOLEAN WmiInitialized : 1;
        
        BOOLEAN BusInterfaceInternal : 1;
        
        BOOLEAN InHwInitialize : 1;
        
        BOOLEAN EnablePPnpPowerSrb : 1;
  • add a function pointer for HW_PASSIVE_INITIALIZE_ROUTINE at the end of the structure by changing it to
    RAID_ADAPTER_PARAMETERS Parameters;
    
    PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitRoutine;
    
} RAID_ADAPTER_EXTENSION, *PRAID_ADAPTER_EXTENSION;
  • edit the file C:\storport\storage\port\raid\port\adapter.h and add the function declaration for RaidGetSrbIoctlFromIrp and RaidAdapterScsiMiniportIoctlWithAddress
//
// Scsi Ioctls
//

NTSTATUS
RaidAdapterScsiMiniportIoctl(
    IN PRAID_ADAPTER_EXTENSION Adapter,
    IN PIRP Irp
    );

NTSTATUS
RaidGetSrbIoctlFromIrp(
    IN PIRP Irp,
    OUT PSRB_IO_CONTROL* SrbIoctlBuffer,
    OUT ULONG* InputLength,
    OUT ULONG* OutputLength
    );

NTSTATUS
RaidAdapterScsiMiniportIoctlWithAddress(
    IN PRAID_ADAPTER_EXTENSION Adapter,
    IN PIRP Irp,
    UCHAR PathId,
    UCHAR TargetId,
    UCHAR Lun
    );
  • edit the file C:\storport\storage\port\raid\port\adapter.c in the function RaidAdapterStartMiniport add the variable BOOLEAN Succ, add a few additional checks and add a function call to HW_PASSIVE_INITIALIZE_ROUTINE. At the end of the function RaidAdapterStartMiniport change the code to
    RaidAdapterAcquireHwInitializeLock (Adapter, &LockContext);
    Adapter->Flags.InHwInitialize = TRUE;

    //
    // Re-enable interrupts.
    //

    RaidAdapterEnableInterrupts (Adapter);

    Status = RaCallMiniportHwInitialize (&Adapter->Miniport);
    Adapter->Flags.InHwInitialize = FALSE;

    if (NT_SUCCESS (Status)) {
        Adapter->Flags.InitializedMiniport = TRUE;
    }
    
    RaidAdapterReleaseHwInitializeLock (Adapter, &LockContext);
    
    if(NT_SUCCESS (Status) && Adapter->HwPassiveInitRoutine != NULL)
    {
        Succ = Adapter->HwPassiveInitRoutine (&Adapter->Miniport.PrivateDeviceExt->HwDeviceExtension);
        Status = RaidNtStatusFromBoolean (Succ);
    }
    
    return Status;
}
  • edit the file C:\storport\storage\port\raid\port\adapter.c and add the function RaidAdapterScsiMiniportIoctlWithAddress after the function RaidAdapterScsiMiniportIoctl
NTSTATUS
RaidAdapterScsiMiniportIoctlWithAddress(
    IN PRAID_ADAPTER_EXTENSION Adapter,
    IN PIRP Irp,
    UCHAR PathId,
    UCHAR TargetId,
    UCHAR Lun
    )
/*++

Routine Description:

    Handle an IOCTL_SCSI_MINIPORT ioctl for this adapter.

Arguments:

    Adapter - The adapter that should handle this IOCTL.

    Irp - Irp representing a SRB IOCTL.

Algorithm:

    Unlike scsiport, which translates the IOCTL into a IRP_MJ_SCSI
    request, then executes the request on the first logical unit in the
    unit list -- we execute the IOCTL "directly" on the adapter. We will
    be able to execute even when the adapter has detected no devices, if
    this matters.

Return Value:

    NTSTATUS code.

--*/
{
    NTSTATUS Status;
    PSCSI_REQUEST_BLOCK Srb;
    PEXTENDED_REQUEST_BLOCK Xrb;
    RAID_MEMORY_REGION SrbExtensionRegion;
    PSRB_IO_CONTROL SrbIoctl;
    ULONG InputLength;
    ULONG OutputLength;

    ASSERT_ADAPTER (Adapter);
    ASSERT (Irp != NULL);
    
    PAGED_CODE();

    Srb = NULL;
    Xrb = NULL;
    RaidCreateRegion (&SrbExtensionRegion);

    Status = RaidGetSrbIoctlFromIrp (Irp, &SrbIoctl, &InputLength, &OutputLength);

    if (!NT_SUCCESS (Status)) {
        Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
        goto done;
    }

    //
    // Begin allocation chain
    //
    
    Srb = RaidAllocateSrb (Adapter->DeviceObject);

    if (Srb == NULL) {
        Status = STATUS_NO_MEMORY;
        goto done;
    }

    Xrb = RaidAllocateXrb (NULL, Adapter->DeviceObject);

    if (Xrb == NULL) {
        Status = STATUS_NO_MEMORY;
        goto done;
    }


    RaidBuildMdlForXrb (Xrb, SrbIoctl, InputLength);

    //
    // Build the srb
    //

    Srb->OriginalRequest = Xrb;
    Srb->Length = sizeof (SCSI_REQUEST_BLOCK);
    Srb->Function = SRB_FUNCTION_IO_CONTROL;
    Srb->PathId = PathId;
    Srb->TargetId = TargetId;
    Srb->Lun = Lun;
    Srb->SrbFlags = SRB_FLAGS_DATA_IN ;
    Srb->DataBuffer = SrbIoctl;
    Srb->DataTransferLength = InputLength;
    Srb->TimeOutValue = SrbIoctl->Timeout;

    //
    // Fill in Xrb fields.
    //

    Xrb->Srb = Srb;
    Xrb->SrbData.OriginalRequest = Srb->OriginalRequest;
    Xrb->SrbData.DataBuffer = Srb->DataBuffer;



    //
    // Srb extension
    //


    Status = RaidDmaAllocateCommonBuffer (&Adapter->Dma,
                                          RaGetSrbExtensionSize (Adapter),
                                          &SrbExtensionRegion);

    if (!NT_SUCCESS (Status)) {
        goto done;
    }

    //
    // Get the VA for the SRB's extension
    //
    
    Srb->SrbExtension = RaidRegionGetVirtualBase (&SrbExtensionRegion);


    //
    // Map buffers, if necessary.
    //
    
    RaidAdapterMapBuffers (Adapter, Irp);


    //
    // Initialize the Xrb's completion event and
    // completion routine.
    //

    KeInitializeEvent (&Xrb->u.CompletionEvent,
                       NotificationEvent,
                       FALSE);

    //
    // Set the completion routine for the Xrb. This effectivly makes the
    // XRB synchronous.
    //
    
    RaidXrbSetCompletionRoutine (Xrb,
                                 RaidXrbSignalCompletion);

    //
    // And execute the Xrb.
    //
    
    Status = RaidAdapterRaiseIrqlAndExecuteXrb (Adapter, Xrb);

    if (NT_SUCCESS (Status)) {
        KeWaitForSingleObject (&Xrb->u.CompletionEvent,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL);

        Status = RaidSrbStatusToNtStatus (Srb->SrbStatus);
    }


done:

    //
    // Set the information length to the min of the output buffer length
    // and the length of the data returned by the SRB.
    //
        
    if (NT_SUCCESS (Status)) {
        Irp->IoStatus.Information = min (OutputLength,
                                         Srb->DataTransferLength);
    } else {
        Irp->IoStatus.Information = 0;
    }

    //
    // Deallocate everything
    //

    if (RaidIsRegionInitialized (&SrbExtensionRegion)) {
        RaidDmaFreeCommonBuffer (&Adapter->Dma,
                                 &SrbExtensionRegion);
        RaidDeleteRegion (&SrbExtensionRegion);
        Srb->SrbExtension = NULL;
    }


    if (Xrb != NULL) {
        RaidFreeXrb (Xrb, FALSE);
        Srb->OriginalRequest = NULL;
    }


    //
    // The SRB extension and XRB must be released before the
    // SRB is freed.
    //

    if (Srb != NULL) {
        RaidFreeSrb (Srb);
        Srb = NULL;
    }

    return RaidCompleteRequest (Irp,
                                
                                Status);
}
  • edit the file C:\storport\storage\port\raid\port\port.c and comment out the function definition for vDbgPrintExWithPrefix
//
// NB: Figure out how to pull in NTRTL.
//
/*
ULONG
vDbgPrintExWithPrefix(
    IN PCH Prefix,
    IN ULONG ComponentId,
    IN ULONG Level,
    IN PCH Format,
    va_list arglist
    );
*/
  • edit the file C:\storport\storage\port\raid\port\port.c and add new notifications to the function StorPortNotification by changing the code to
//------------------------------------------------------------------------------
// 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only the offset of Adapter->Interrupt does not match 100%
//------------------------------------------------------------------------------
KIRQL RaidAdapterAcquireInterruptLock(RAID_ADAPTER_EXTENSION *Adapter)
{
    KIRQL irql;
    irql = PASSIVE_LEVEL;
    if(Adapter->Interrupt)
    {
        irql = KeAcquireInterruptSpinLock(Adapter->Interrupt);
    }

    return irql;
}


//------------------------------------------------------------------------------
// 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only the offset of Adapter->Interrupt does not match 100%
//------------------------------------------------------------------------------
VOID RaidAdapterReleaseInterruptLock(RAID_ADAPTER_EXTENSION *Adapter,KIRQL OldIrq)
{
    if(Adapter->Interrupt)
    {
        KeReleaseInterruptSpinLock(Adapter->Interrupt,OldIrq);
    }
}


//------------------------------------------------------------------------------
// 100% match Windows 7 SP1 x86 retail storport.sys
//------------------------------------------------------------------------------
VOID StorAcquireSpinLock(PVOID HwDeviceExtension,STOR_SPINLOCK SpinLock,PVOID LockContext,
                         PSTOR_LOCK_HANDLE LockHandle)
{
    PRAID_ADAPTER_EXTENSION Adapter;

    Adapter = RaidpPortGetAdapter (HwDeviceExtension);

    LockHandle->Lock = SpinLock;

    switch(LockHandle->Lock)
    {
        case InterruptLock:
        {
            LockHandle->Context.OldIrql = RaidAdapterAcquireInterruptLock(Adapter);
            break;
        }
        case StartIoLock:
        {
            RaidAdapterAcquireStartIoLock(Adapter,(PLOCK_CONTEXT)&LockHandle->Context);
            break;
        }
        case DpcLock:
        {
            KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)&((PSTOR_DPC)LockContext)->Lock,
                                           (PKLOCK_QUEUE_HANDLE)&LockHandle->Context);
            break;
        }
    }
}


//------------------------------------------------------------------------------
// 100% match Windows 7 SP1 x86 retail storport.sys
//------------------------------------------------------------------------------
VOID StorReleaseSpinLock(PVOID HwDeviceExtension,PSTOR_LOCK_HANDLE LockHandle)
{
    PRAID_ADAPTER_EXTENSION Adapter;

    Adapter = RaidpPortGetAdapter (HwDeviceExtension);

    switch(LockHandle->Lock)
    {
        case InterruptLock:
        {
            RaidAdapterReleaseInterruptLock(Adapter,LockHandle->Context.OldIrql);
            break;
        }
        case StartIoLock:
        {
            RaidAdapterReleaseStartIoLock(Adapter,(PLOCK_CONTEXT)&LockHandle->Context);
            break;
        }
        case DpcLock:
        {
            KeReleaseInStackQueuedSpinLock((PKLOCK_QUEUE_HANDLE)&LockHandle->Context);
            break;
        }
    }
}


//------------------------------------------------------------------------------
// 100% match Windows 7 SP1 x86 retail storport.sys
//------------------------------------------------------------------------------
VOID StorInitializeDpc(PVOID HwDeviceExtension,PSTOR_DPC Dpc,PHW_DPC_ROUTINE HwDpcRoutine)
{
    KeInitializeDpc((PRKDPC)Dpc, (PKDEFERRED_ROUTINE) HwDpcRoutine, HwDeviceExtension);
    Dpc->Lock = 0;
}


//------------------------------------------------------------------------------
// 100% match Windows 7 SP1 x86 retail storport.sys
//------------------------------------------------------------------------------
BOOLEAN StorIssueDpc(PVOID HwDeviceExtension,PSTOR_DPC Dpc,PVOID SystemArgument1,
                     PVOID SystemArgument2)
{
    return KeInsertQueueDpc((PRKDPC)Dpc,SystemArgument1,SystemArgument2);
}


//------------------------------------------------------------------------------
// 100% match Windows Server 2003 SP2 x86 retail storport.sys
//------------------------------------------------------------------------------
#define TRACE_MESSAGE_SEQUENCE              1  // Message should include a sequence number
#define TRACE_MESSAGE_GUID                  2  // Message includes a GUID
#define TRACE_MESSAGE_TIMESTAMP             8  // Message includes a timestamp
#define TRACE_MESSAGE_SYSTEMINFO            32 // Message includes system information TID,PID
__drv_maxIRQL(HIGH_LEVEL) NTKERNELAPI NTSTATUS WmiTraceMessage(__in TRACEHANDLE LoggerHandle,
__in ULONG MessageFlags,__in LPCGUID MessageGuid,__in USHORT MessageNumber,...);
NTSTATUS WPP_SF_(TRACEHANDLE LoggerHandle,USHORT MessageNumber,LPCGUID MessageGuid)
{
    return WmiTraceMessage(LoggerHandle,TRACE_MESSAGE_SEQUENCE | TRACE_MESSAGE_GUID |
                           TRACE_MESSAGE_TIMESTAMP | TRACE_MESSAGE_SYSTEMINFO,MessageGuid,
                           MessageNumber,NULL);
}


//------------------------------------------------------------------------------
// near 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only difference is that WPP tracing is not done correctly
//------------------------------------------------------------------------------
PULONG _WPP_GLOBAL_Control = NULL;
TRACEHANDLE unk_20008 = 0x7CF4010094F40100;
GUID aGnkh = {0x68AAAD67,0x0700,0x6A21,0x5D,0x1A,0x7E,0xC9,0xB8,0xA2,0x38,0x9D};
BOOLEAN StorEnablePassiveInitialization(PVOID HwDeviceExtension,
                                        PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitializeRoutine)
{
    PRAID_ADAPTER_EXTENSION Adapter;

    Adapter = RaidpPortGetAdapter (HwDeviceExtension);

    if(Adapter->Flags.InHwInitialize == FALSE)
    {
        if(_WPP_GLOBAL_Control != NULL)
        {
            WPP_SF_(unk_20008,0x0B,&aGnkh);
        }

        return FALSE;
    }

    Adapter->HwPassiveInitRoutine = HwPassiveInitializeRoutine;

    return TRUE;
}


VOID
StorPortNotification(
    IN SCSI_NOTIFICATION_TYPE NotificationType,
    IN PVOID HwDeviceExtension,
    ...
    )
{
    PRAID_ADAPTER_EXTENSION Adapter;
    PSCSI_REQUEST_BLOCK Srb;
    PHW_INTERRUPT HwTimerRoutine;
    ULONG Timeout;
    PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitializeRoutine;
    PSTOR_DPC Dpc;
    PHW_DPC_ROUTINE HwDpcRoutine;
    PVOID SystemArgument1;
    PVOID SystemArgument2;
    PLONG Succ;
    STOR_SPINLOCK SpinLock;
    PVOID LockContext;
    PSTOR_LOCK_HANDLE LockHandle;
    va_list ap;

    Adapter = RaidpPortGetAdapter (HwDeviceExtension);
    
    va_start(ap, HwDeviceExtension);

    switch (NotificationType) {

        case EnablePassiveInitialization:
            HwPassiveInitializeRoutine = va_arg (ap, PHW_PASSIVE_INITIALIZE_ROUTINE);
            Succ = va_arg (ap, PLONG);
            *Succ = StorEnablePassiveInitialization(HwDeviceExtension,
                                                    HwPassiveInitializeRoutine);
            break;

        case InitializeDpc:
            Dpc = va_arg (ap, PSTOR_DPC);
            HwDpcRoutine = va_arg (ap, PHW_DPC_ROUTINE);
            StorInitializeDpc(HwDeviceExtension, Dpc, HwDpcRoutine);
            break;

        case IssueDpc:
            Dpc = va_arg (ap, PSTOR_DPC);
            SystemArgument1 = va_arg (ap, PVOID);
            SystemArgument2 = va_arg (ap, PVOID);
            Succ = va_arg (ap, PLONG);
            *Succ = StorIssueDpc(HwDeviceExtension, Dpc, SystemArgument1, SystemArgument2);
            break;

        case AcquireSpinLock:
            SpinLock = va_arg (ap, STOR_SPINLOCK);
            LockContext = va_arg (ap, PVOID);
            LockHandle = va_arg (ap, PSTOR_LOCK_HANDLE);
            StorAcquireSpinLock(HwDeviceExtension, SpinLock, LockContext, LockHandle);
            break;

        case ReleaseSpinLock:
            LockHandle = va_arg (ap, PSTOR_LOCK_HANDLE);
            StorReleaseSpinLock(HwDeviceExtension, LockHandle);
            break;

        case RequestComplete:
            Srb = va_arg (ap, PSCSI_REQUEST_BLOCK);
            RaidAdapterRequestComplete (Adapter, RaidGetAssociatedXrb (Srb));
            break;

  • edit the file C:\storport\storage\port\raid\port\unit.c and add function RaUnitScsiMiniportIoctl to ALLOC_PRAGMA
#pragma alloc_text(PAGE, RaUnitStorageQueryPropertyIoctl)
#pragma alloc_text(PAGE, RaUnitScsiMiniportIoctl)
  • edit the file C:\storport\storage\port\raid\port\unit.c and add function call RaUnitScsiMiniportIoctl to function RaUnitDeviceControlIrp
        case IOCTL_STORAGE_QUERY_PROPERTY:
            Status = RaUnitStorageQueryPropertyIoctl (Unit, Irp);
            break;

        case IOCTL_SCSI_MINIPORT:
            Status = RaUnitScsiMiniportIoctl (Unit, Irp);
            break;
  • edit the file C:\storport\storage\port\raid\port\unit.c and add function RaUnitScsiMiniportIoctl after the function RaUnitStorageQueryPropertyIoctl
//------------------------------------------------------------------------------
// 100% correct builded on WDK version 7600.16385.1 and Windows 7 free build
// environment
//------------------------------------------------------------------------------
NTSTATUS RaUnitScsiMiniportIoctl(IN PRAID_UNIT_EXTENSION Unit,IN PIRP Irp)
{
    NTSTATUS Status;
    PSRB_IO_CONTROL SrbIoctl;
    ULONG InputLength;
    ULONG OutputLength;

    PAGED_CODE();
    ASSERT_UNIT(Unit);

    Status = RaidGetSrbIoctlFromIrp(Irp,&SrbIoctl,&InputLength,&OutputLength);
    if(!NT_SUCCESS(Status))
    {
        return RaidCompleteRequest(Irp,STATUS_INVALID_PARAMETER);
    }

    // because of this check Windows 7 is not capable to handle CrystalDiskInfo SMART commands
    //if(RtlCompareMemory(SrbIoctl->Signature,"HYBRDISK",8) == 8 &&
    //   SrbIoctl->ControlCode == IOCTL_SCSI_MINIPORT_NVCACHE)
    // we could change this like follows to make it work with CrystalDiskInfo's SMART commands
    // at the end we decided to comment out this code completely to make it work always
    //if(RtlCompareMemory(SrbIoctl->Signature,"SCSIDISK",8) == 8 &&
    //   SrbIoctl->ControlCode == IOCTL_SCSI_MINIPORT)
    //{
        if(Irp->RequestorMode != KernelMode)
        {
            return RaidCompleteRequest(Irp,STATUS_ACCESS_DENIED);
        }
    //}
    //else
    //{
        //return RaUnitUnknownIoctl(Unit,Irp);
    //}

    return RaidAdapterScsiMiniportIoctlWithAddress(Unit->Adapter,Irp,Unit->Address.PathId,
                                                   Unit->Address.TargetId,Unit->Address.Lun);
}
  • edit the file C:\storport\storage\port\raid\port\power.c and change the function RaidUnitSetPowerIrp so that it does not trigger the if in case of PowerSystemShutdown
if (PowerState.SystemState > PowerSystemShutdown) {
  • edit the file C:\storport\storage\port\raid\port\power.c and change the function RaidUnitSetSystemPowerIrp to
//------------------------------------------------------------------------------
// near 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only trace function WmiTraceMessage is missing
//------------------------------------------------------------------------------
NTSTATUS
RaidUnitSetSystemPowerIrp(
    IN PRAID_UNIT_EXTENSION Unit,
    IN PIRP Irp
    )
{
    NTSTATUS Status;
    POWER_STATE DevicePowerState;
    POWER_STATE SystemPowerState;
	
    DebugPower (("Unit %p, Irp %p, Set System Power, ret SUCCESS\n",
                 Unit, Irp));

    SystemPowerState = RaidPowerStateFromIrp (Irp);
    DevicePowerState.DeviceState = DeviceStateTable [SystemPowerState.SystemState];

    Status = PoRequestPowerIrp (Unit->DeviceObject, IRP_MN_SET_POWER, DevicePowerState, NULL, Irp, NULL);
    if (Status == STATUS_PENDING)
    {
    	Status = STATUS_SUCCESS;	
    }
    PoStartNextPowerIrp (Irp);
    return RaidCompleteRequest (Irp,  Status);
}
  • edit the file C:\storport\storage\port\raid\port\unit.c and change the function RaUnitFlushQueueSrb to
//------------------------------------------------------------------------------
// 100% match Windows Server 2003 SP2 x86 retail storport.sys
// only the offset of &Unit->IoQueue does not match 100%
//------------------------------------------------------------------------------
NTSTATUS
RaUnitFlushQueueSrb(
    IN PRAID_UNIT_EXTENSION Unit,
    IN PIRP FlushIrp
    )
/*++

Routine Description:

    Flush the pending queue, canceling all requests.

Arguments:

    Unit - Supplies pointer to unit to flush.

    FlushIrp - Supplies the IRP representing the flush queue request.

Return Value:

    NTSTATUS code.

Environment:

    DISPATCH_LEVEL or below.

--*/
{
    PIRP Irp;
    PSCSI_REQUEST_BLOCK Srb;
    LIST_ENTRY listHead;
    PIRP nextIrp = NULL;
    PLIST_ENTRY nextEntry;

    ASSERT (RaidIsUnitQueueFrozen (Unit));

    //
    // Flush all entries.
    //
    
    InitializeListHead (&listHead);
    for (Irp = RaidRemoveIoQueue (&Unit->IoQueue);
         Irp != NULL;
         Irp = RaidRemoveIoQueue (&Unit->IoQueue)) {

        InsertTailList (&listHead,&Irp->Tail.Overlay.ListEntry);
    }

    RaidThawUnitQueue (Unit);

    while (!IsListEmpty (&listHead))
    {
        nextEntry = RemoveHeadList (&listHead);
        nextIrp = CONTAINING_RECORD (nextEntry, IRP, Tail.Overlay.ListEntry);

        Srb = RaidSrbFromIrp (nextIrp);
        Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
        nextIrp->IoStatus.Information = 0;
        RaidCompleteRequest (nextIrp,  STATUS_UNSUCCESSFUL);

        nextIrp = NULL;
    }

    // This solves a bug with CD-/DVD-ROM drives where the original storport
    // driver uses the Irp instead of the FlushIrp here. This throws an
    // exception, because Irp can be NULL.
    Srb = RaidSrbFromIrp (FlushIrp);
    Srb->SrbStatus = SRB_STATUS_SUCCESS;
    FlushIrp->IoStatus.Information = 0;
    return RaidCompleteRequest (FlushIrp,  STATUS_SUCCESS);
}
  • Start > Programs > Windows Driver Kits > WDK 7600.16385.1 > Build Environments > Windows Server 2003 > x86 Free Build Environment
  • type the following commands in the WDK build environment:
  • cd C:\storport\storage
    build -cewgZ
  • the final storport.sys driver is located in C:\storport\storage\port\raid\port\objfre_wnet_x86\i386

7.) Patch original Windows 7 SP1 x86 storport to make it work with Windows 2003 Server
----------------------------------------------------------------------------------------------

We patch the original Windows 7 SP1 x86 storport.sys Version 6.1.7601.17514 to function properly with Windows 2003 Server. Therefore we have to do the following steps:

  • download Dependency Walker v2.2
  • unpack depends22_x86.zip to C:\depends22_x86
  • run C:\depends22_x86\depends.exe > Menu > File > Open... > choose Windows 7 SP1 x86 storport.sys > Open > we see a message that errors were detected > OK
  • on the upper left window click on NTOSKRNL.EXE > in the upper right window sort the column Function and after that the column PI > this way all red marked imports are on top sorted by name
  • we have the following missing imports in NTOSKRNL.EXE:
  • replace the missing imports like NTOSKRNL_Emu does it and compile ntoskrn7.sys
  • patch the following bytes in storport.sys of Windows 7 SP1 x86:
  Offset    Size in Bytes    Comment
  -----------------------------------------------------------------------------------------------------------------
  0x120     2                MajorSubsystemVersion and MinorSubsystemVersion changed from 6.1 to 5.1
  0x130     4                checksum of executable corrected
  0x170     8                Directory Security (Certificate Table) RVA and size set to zero, because the
                             certificate was removed 
  0xFE79    2                disable compare of SRB_IO_CONTROL Signature to string "HYBRDISK" to enable SMART
                             commands for CrystalDiskInfo
  0xFE85    2                disable compare of SRB_IO_CONTROL ControlCode to IOCTL_SCSI_MINIPORT_NVCACHE to
                             enable SMART commands for CrystalDiskInfo
  0x1CE9B   5                imports section ntoskrnl.exe replaced by ntoskrn7.sys
  0x1CE05   0                the security cookie check is not patched for a Windows 7 driver
  0x22A00   0x1B80           only present in original driver file, code signing certificate removed in patched
                             driver file
The driver ntoskrn7.sys is compiled with WDK version 7600.16385.1 and the Windows Server 2003 x86 Free Build Environment. The example source code is included in the file storport_for_Windows_2003_with_SMART_support.rar. This archive also holds the prepatched storport.sys of Windows 7 SP1 x86.


8.) How to apply the storport patches
-----------------------------------------

To apply the Windows 2003 Server storport patch based on the leaked source code do the following:
  • rename C:\WINDOWS\system32\dllcache\storport.sys to storport.sys_
  • rename C:\WINDOWS\system32\drivers\storport.sys to storport.sys_
  • we will get a Windows File Protection warning dialog where we press Cancel and Yes
  • copy the patched storport_for_Windows_2003_with_SMART_support\storport\bin\storport.sys to C:\WINDOWS\system32\drivers
  • we will get a Windows File Protection warning dialog where we press Cancel and Yes
  • reboot the computer
We tested the SSD read/write performance afterwards with CrystalDiskMark 6.0.1.



To apply the Windows 7 storport and ntoskrn7 patch do the following:
  • rename C:\WINDOWS\system32\dllcache\storport.sys to storport.sys_
  • rename C:\WINDOWS\system32\drivers\storport.sys to storport.sys_
  • we will get a Windows File Protection warning dialog where we press Cancel and Yes
  • copy the patched storport_for_Windows_2003_with_SMART_support\ntoskrnl7\bin\storport.sys to C:\WINDOWS\system32\drivers
  • we will get a Windows File Protection warning dialog where we press Cancel and Yes
  • copy storport_for_Windows_2003_with_SMART_support\ntoskrnl7\bin\ntoskrn7.sys to C:\WINDOWS\system32\drivers
  • reboot the computer
Here we also tested the SSD read/write performance afterwards with CrystalDiskMark 6.0.1.



Attention: These drivers have not been tested extensively and should not be use in a production environment! These patches were done for educational and proof of concept purposes only.

9.) Credits and Download Links
----------------------------------

A BIG THANKS goes out to the following geniuses: Much appreciated!



The complete storport package with all necessary files can be downloaded from Sourceforge.

Thanks for your attention and interest in this topic.
Greets Kai Schtrom

Version 1.1 May 05, 2021