前段时间刚配了一台新电脑,由于资金有限没能配一块心仪已久的固态硬盘,没能感受到飞一般的感觉,总不甘心,仔细想想,我配的内存比较大呀,如果能把内存的一部分让出来当作硬盘使用,岂不比固态硬盘速度更快,那么就让我们开始吧。
首先,我们要做的就是写一个硬盘控制器的驱动,我们知道,存储类型的驱动一般都遵守 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 到一个文件里,设备重启后再加载,即可达到固化的效果。其实网上有非常多的成熟的产品可以使用,自己写一个也仅仅是为了练习而已,驱动没有经过大量测试,可能会产生蓝屏,可以在虚拟机上修改并完善它。