前段时间刚配了一台新电脑,由于资金有限没能配一块心仪已久的固态硬盘,没能感受到飞一般的感觉,总不甘心,仔细想想,我配的内存比较大呀,如果能把内存的一部分让出来当作硬盘使用,岂不比固态硬盘速度更快,那么就让我们开始吧。

        首先,我们要做的就是写一个硬盘控制器的驱动,我们知道,存储类型的驱动一般都遵守 class/port/miniport driver 这样的结构,微软已经完成了磁盘类的驱动,以及 SCSI 总线的 Port 驱动,我们只需要完成 SCSI 总线上硬盘控制器的 Miniport 驱动就可以了。拿出 DDK 的源码分析一遍,微软果然不负众望地提供了一个驱动的源码,我用的是DDK 7600.16385.1,里面有一个 ramdisk 的源码,但读起来就发现,它是用 wdf 框架来实现的,我对 wdf 框架不熟悉,决定自己用 wdm 框架来重新实现一遍。

        接着便是各种资料的查询,最后终于摸清了SCSI miniport 驱动的大体框架,为了给一个极致简单的框架,我省去了许多冗余的代码,代码如下:

#define DBG 1

#include <ntddk.h>
#include <srb.h>
#include <scsi.h>

#define MODULE_NAME_PREFIX       "RamDisk: "
#define KdPrintThisFunction()    KdPrint((MODULE_NAME_PREFIX"%s\n", __FUNCTION__))

#define RAMDISK_SECTOR_SIZE      512
#define RAMDISK_CAPACITY         16 * 1024 *1024        

//typedef struct _DEVICE_EXTENSION {

//} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// Miniport 的一些回调函数
BOOLEAN Initialize(__in PVOID DeviceExtension);
BOOLEAN ResetBus(__in PVOID DeviceExtension, __in ULONG PathId);
BOOLEAN StartIo(__in PVOID DeviceExtension, __in PSCSI_REQUEST_BLOCK Srb);
BOOLEAN Interrupt(__in PVOID DeviceExtension);
ULONG FindAdapter(
    __in PVOID DeviceExtension,
    __in PVOID HwContext,
    __in PVOID BusInformation,
    __in PCHAR ArgumentString,
    __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
    __out PBOOLEAN Again
    );
BOOLEAN AdapterState(__in PVOID DeviceExtension, __in PVOID Context, __in BOOLEAN SaveState);
SCSI_ADAPTER_CONTROL_STATUS AdapterControl(PVOID DeviceExtension,SCSI_ADAPTER_CONTROL_TYPE ctlType,PVOID pParameters);


VOID DriverUnload(__in struct _DRIVER_OBJECT *DriverObject);

PVOID RamDiskMemroy;

NTSTATUS 
DriverEntry( 
    __in struct _DRIVER_OBJECT  *DriverObject,
    __in PUNICODE_STRING  RegistryPath 
    )
{
    HW_INITIALIZATION_DATA HwInitData;

    KdPrintThisFunction();

    RtlZeroMemory(&HwInitData, sizeof(HW_INITIALIZATION_DATA));
    HwInitData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);

    HwInitData.HwInitialize = Initialize;
    HwInitData.HwResetBus = ResetBus;
    HwInitData.HwStartIo = StartIo;
    HwInitData.HwInterrupt = NULL;                         // 不需要中断服务
    HwInitData.HwFindAdapter = FindAdapter;
    HwInitData.HwAdapterState = AdapterState;
    HwInitData.HwAdapterControl = AdapterControl;

    HwInitData.AdapterInterfaceType = Isa;
    HwInitData.DeviceExtensionSize = 0;
    HwInitData.SrbExtensionSize = 0;    
    HwInitData.NumberOfAccessRanges = 0;

    HwInitData.MapBuffers = TRUE;
    HwInitData.NeedPhysicalAddresses = FALSE;
    HwInitData.TaggedQueuing = FALSE;
    HwInitData.AutoRequestSense = TRUE;
    HwInitData.MultipleRequestPerLu = FALSE;
    HwInitData.ReceiveEvent = FALSE;
    
    // 初始化
    ScsiPortInitialize(DriverObject, RegistryPath, &HwInitData, NULL);

    DriverObject->DriverUnload = DriverUnload;
 
    RamDiskMemroy = ExAllocatePool(NonPagedPool, RAMDISK_CAPACITY);
    if (RamDiskMemroy == NULL) {
        KdPrint(("RamDisk: Allocate memory failed!\n"));
        return STATUS_FAILED_DRIVER_ENTRY;
    }

    KdPrint(("MyRamDisk: RamDiskMemroy - 0x%p\n", RamDiskMemroy));

    return STATUS_SUCCESS;
}

VOID
DriverUnload(
    __in struct _DRIVER_OBJECT *DriverObject
    )
{
    KdPrintThisFunction();
    ExFreePool(RamDiskMemroy);
}

BOOLEAN 
Initialize(
    __in PVOID DeviceExtension
    )
{
    KdPrintThisFunction();
    return TRUE;
}

BOOLEAN
ResetBus(
    __in PVOID DeviceExtension, 
    __in ULONG PathId
    )
{
    KdPrintThisFunction();
    return TRUE;
}

BOOLEAN
ReadCapacityData(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    PREAD_CAPACITY_DATA CapacityData = (PREAD_CAPACITY_DATA)Srb->DataBuffer;
    ULONG Value = 0;

    KdPrintThisFunction();

    // this two value should in Big Endian
    Value = RAMDISK_CAPACITY / RAMDISK_SECTOR_SIZE - 1;
    REVERSE_LONG(&Value);
    CapacityData->LogicalBlockAddress = Value;
    Value = RAMDISK_SECTOR_SIZE;
    REVERSE_LONG(&Value);
    CapacityData->BytesPerBlock = Value;

    return TRUE;
}

BOOLEAN
Inquiry(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    PINQUIRYDATA InquiryData = (PINQUIRYDATA)Srb->DataBuffer;
    PCDB Cdb = &Srb->Cdb;

    KdPrintThisFunction();

    InquiryData->DeviceType = DIRECT_ACCESS_DEVICE;
    InquiryData->DeviceTypeQualifier = DEVICE_CONNECTED;
    InquiryData->DeviceTypeModifier = 0;
    InquiryData->RemovableMedia = 0;
    InquiryData->ResponseDataFormat = 2;
    InquiryData->Versions = 0;
    InquiryData->AdditionalLength = sizeof(INQUIRYDATA) - 5;
    
    // 这些数据关系到系统中设备管理器的显示
    RtlMoveMemory(InquiryData->VendorId,"HENZOX",6);
    RtlMoveMemory(InquiryData->ProductId,"RamDisk - Henzox",16);
    RtlMoveMemory(InquiryData->ProductRevisionLevel,"1010",4);

    KdPrint(("Inquiry: succeeded(target-lun)=(%d,%d)\n", Srb->TargetId, Srb->Lun));

    return TRUE;
}

BOOLEAN
ReadDisk(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    ULONG Start = 0;                     // 开始扇区
    USHORT Count = 0;                    // 读取大小,以扇区为单位

    PCDB Cdb = &Srb->Cdb[0];
    Start = *(PULONG)&Cdb->CDB10.LogicalBlockByte0;
    REVERSE_LONG(&Start);
    Count = *(PUSHORT)&Cdb->CDB10.TransferBlocksMsb;
    REVERSE_SHORT(&Count);

    if ((Start + Count) * RAMDISK_SECTOR_SIZE > RAMDISK_CAPACITY) {
        KdPrint(("ReadDisk: overflow!\n"));
        return FALSE;
    }
    // Count * RAMDISK_SECTOR_SIZE equals to Srb->DataTransferLength
    KdPrint(("ReadDisk: Start - %d, Size - %d\n", Start * RAMDISK_SECTOR_SIZE, Srb->DataTransferLength));
    RtlMoveMemory(Srb->DataBuffer, (PUCHAR)RamDiskMemroy + Start * RAMDISK_SECTOR_SIZE, Srb->DataTransferLength);
    return TRUE;
}

BOOLEAN
WriteDisk(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    ULONG Start = 0;                     // 开始地址
    USHORT Count = 0;                    // 读取大小,以扇区为单位

    PCDB Cdb = &Srb->Cdb[0];
    Start = *(PULONG)&Cdb->CDB10.LogicalBlockByte0;
    REVERSE_LONG(&Start);
    Count = *(PUSHORT)&Cdb->CDB10.TransferBlocksMsb;
    REVERSE_SHORT(&Count);

    if (Start + Count * RAMDISK_SECTOR_SIZE > RAMDISK_CAPACITY) {
        KdPrint(("WriteDisk: overflow!\n"));
        return FALSE;
    }

    KdPrint(("WriteDisk: Start - %d, Size - %d\n", Start * RAMDISK_SECTOR_SIZE, Srb->DataTransferLength));
    RtlMoveMemory((PUCHAR)RamDiskMemroy + Start * RAMDISK_SECTOR_SIZE, Srb->DataBuffer, Srb->DataTransferLength);
    return TRUE;

}

BOOLEAN
ExecuteScsi(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    BOOLEAN ReturnValue = FALSE;
    PCDB Cdb = (PCDB)&Srb->Cdb[0];
    
    KdPrintThisFunction();

    switch (Cdb->CDB10.OperationCode) {
    case 0x28:
        // Read disk
        ReturnValue = ReadDisk(Srb);
        break;
    case 0x2A:
        // Write disk
        ReturnValue = WriteDisk(Srb);
        break;
    case 0x25:
        // Read the capacity of the disk
        ReturnValue = ReadCapacityData(Srb);
        break;
    case 0x12:
        // Disk inquiry
        ReturnValue = Inquiry(Srb);
        break;
    default:
        KdPrint(("ExecuteScsi: Unknown operation code(0x%p)\n", Cdb->CDB10.OperationCode));
        ReturnValue = TRUE;
    }

    return ReturnValue;
}

BOOLEAN
DoIoControl(
    PSCSI_REQUEST_BLOCK Srb
    )
{
    KdPrintThisFunction();

    return TRUE;
}

BOOLEAN 
StartIo(
    __in PVOID DeviceExtension, 
    __in PSCSI_REQUEST_BLOCK Srb
    )
{
    BOOLEAN ReturnValue = TRUE;

    KdPrintThisFunction();

    Srb->SrbStatus = SRB_STATUS_SUCCESS;
    Srb->ScsiStatus = SCSISTAT_GOOD;

    switch(Srb->Function) {
    case SRB_FUNCTION_SHUTDOWN:
        KdPrint(("StartIo: SRB_FUNCTION_SHUTDOWN\n"));
        break;
    case SRB_FUNCTION_FLUSH:
        KdPrint(("StartIo: SRB_FUNCTION_FLUSH\n"));
        break;
    case SRB_FUNCTION_EXECUTE_SCSI:
        // 执行 SCSI 命令
        ReturnValue = ExecuteScsi(Srb);
        break;
    case SRB_FUNCTION_IO_CONTROL:
        Srb->SrbStatus = SRB_STATUS_PENDING;
        ReturnValue = DoIoControl(Srb);
        break;

    default:
        Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
    }

    ScsiPortNotification(RequestComplete, DeviceExtension, Srb);
    ScsiPortNotification(NextRequest, DeviceExtension);
    return ReturnValue;
}

BOOLEAN
Interrupt(
    __in PVOID DeviceExtension
    )
{
    KdPrintThisFunction();
    return TRUE;
}

ULONG FindAdapter(
    __in PVOID DeviceExtension,
    __in PVOID HwContext,
    __in PVOID BusInformation,
    __in PCHAR ArgumentString,
    __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
    __out PBOOLEAN Again
    )
{
    KdPrintThisFunction();

    ConfigInfo->AdapterInterfaceType = Isa;
    ConfigInfo->AlignmentMask = 0x00000003;
    ConfigInfo->AutoRequestSense = TRUE;
    ConfigInfo->BufferAccessScsiPortControlled = FALSE;
    ConfigInfo->BusInterruptLevel = 0;
    ConfigInfo->BusInterruptVector = 0;
    ConfigInfo->Dma32BitAddresses = TRUE;
    ConfigInfo->Master = TRUE;
    ConfigInfo->CachesData = TRUE;
    ConfigInfo->NumberOfBuses = 1;
    ConfigInfo->MaximumNumberOfTargets = 1;
    ConfigInfo->MaximumTransferLength = 0x10000;
    ConfigInfo->MultipleRequestPerLu = FALSE;
    ConfigInfo->NumberOfPhysicalBreaks = 0x00F8;
    ConfigInfo->ScatterGather = TRUE;
    ConfigInfo->TaggedQueuing = FALSE;

    *Again = FALSE;
    return SP_RETURN_FOUND;
}

SCSI_ADAPTER_CONTROL_STATUS 
AdapterControl(
    PVOID DeviceExtension,
    SCSI_ADAPTER_CONTROL_TYPE CtlType,
    PVOID Parameters
    )
{
    PSCSI_SUPPORTED_CONTROL_TYPE_LIST ScsiList=NULL;
    SCSI_ADAPTER_CONTROL_STATUS status = ScsiAdapterControlSuccess;

    KdPrintThisFunction();

    switch (CtlType<span style="font-family: Arial, Helvetica, sans-serif;">) </span>{
    case ScsiQuerySupportedControlTypes:
        KdPrint(("AdapterControl: ScsiQuerySupportedControlTypes\n"));
        ScsiList = (PSCSI_SUPPORTED_CONTROL_TYPE_LIST)Parameters;
        <span style="font-family: Arial, Helvetica, sans-serif;">ScsiList</span>->SupportedTypeList[ScsiStopAdapter] = TRUE;
        ScsiList->SupportedTypeList[ScsiRestartAdapter] = TRUE;
        ScsiList->SupportedTypeList[ScsiQuerySupportedControlTypes] = TRUE;
        break;

    case ScsiStopAdapter:
        KdPrint(("AdapterControl: ScsiStopAdapter\n"));
        break;

    case ScsiRestartAdapter:
        KdPrint(("AdapterControl: ScsiRestartAdapter\n"));
        break;

    default:
        status = ScsiAdapterControlUnsuccessful;
        break;
    }

    return status;
}

BOOLEAN
AdapterState(
    __in PVOID DeviceExtension,
    __in PVOID Context,
    __in BOOLEAN SaveState
    )
{
    KdPrintThisFunction();
    return TRUE;
}

        简要地说明一下,Scsiminiport 驱动其实很简单,就是调用ScsiPortInitialize 初始化之后,完成各种 Srb 命令就可以了,源码中本来想用中文注释和英文注释混杂,这是我的一个不好习惯,但注释明了,极易读懂。

        编译出来之后,可以直接安装,然后在磁盘管理里会新出现一个磁盘,初始化这个磁盘就可以了,我只设了 16M 以用来实现,可以通过修改源码达到你想要的大小,把它用来当浏览器的临时目录存储盘是相当不错的,每次重启,该盘会销毁,在微软的 wdf 源码中有如何使该盘自动初始化的源码,你也可以添加一些其它功能,比如关机时把该盘中的功能 dump 到一个文件里,设备重启后再加载,即可达到固化的效果。其实网上有非常多的成熟的产品可以使用,自己写一个也仅仅是为了练习而已,驱动没有经过大量测试,可能会产生蓝屏,可以在虚拟机上修改并完善它。