wdk中ramdisk代码解读

入口函数,即驱动加载函数

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path
                   to driver-specific key in the registry

Return Value:

    STATUS_SUCCESS if successful.

--*/

{
    WDF_DRIVER_CONFIG config;

    KdPrint(("Windows Ramdisk Driver - Driver Framework Edition.
"));
    KdPrint(("Built %s %s
", __DATE__, __TIME__));


    WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd );

    return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
}

初始化可配置变量config,指定RamDiskEvtDeviceAdd的地址,这里的RamDiskEvtDeviceAdd相当于wdm中AddDevice回调函数,当即插即用管理器发现了一个新设备 ,则调用的函数。然后调用WdfDriverCreate返回,我的理解是WdfDriverCreate创建了WDF驱动框架。

接下来是RamDiskEvtDeviceAdd函数

NTSTATUS
RamDiskEvtDeviceAdd(
    IN WDFDRIVER Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
{
    //将要建立的设备对象的属性描述变量
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    //将要调用的各种函数的状态返回值
    NTSTATUS                status;
    //将要建立的设备对象
    WDFDEVICE               device;
    //将要建立的队列对象的属性描述变量
    WDF_OBJECT_ATTRIBUTES   queueAttributes;
    //将要简历的队列配置变量
    WDF_IO_QUEUE_CONFIG     ioQueueConfig;
    //设备对象的扩展域的指针
    PDEVICE_EXTENSION       pDeviceExtension;
    //将要建立的队列扩展域指针
    PQUEUE_EXTENSION        pQueueContext = NULL;
    //将要建立的队列对象
    WDFQUEUE                queue;
    //初始化字符串
     DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
    //保证这个函数可以操作paged内存
    PAGED_CODE();
    //防止产生警告
     UNREFERENCED_PARAMETER(Driver);
    //初始化设备的名字 
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //磁盘设备的类型  必须是FILE_DEVICE_DISK
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
    //设备的io类型   WdfDeviceIoBuffered 使用缓冲区接受数据  WdfDeviceIoDirect 直接接收数据 IRP所携带的缓冲区  可以直接使用
    //WdfDeviceIoBufferedOrDirect  前面两种方式   都可以使用
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
    //Exclusive:独占    设置设备为非独占的
    WdfDeviceInitSetExclusive(DeviceInit, FALSE);
    //设置属性描述变量   就是说  设备对象的扩展  使用什么样的数据结构存储数据
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
    //制定设备的清理回调函数
    deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;
    
    //建立这个设备
    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //将指针指向新建立设备的设备扩展
    pDeviceExtension = DeviceGetExtension(device);
    //将队列的配置对象初始化为默认值
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
        &ioQueueConfig,
        WdfIoQueueDispatchSequential
        );
    //wdf中我们需要将发往自己创建的设备的请求处理函数  在队列对象的设置对象中设置
    
    //我们暂时只关心IoDeviceControl和读写事件
    ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
    ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;
    ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;
    //指定这个队列设备的属性描述对象
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);
    
    //创建这个队列对象  这里第一个参数是设备对象   说明这个队列在创建的时候   已经和设备绑定了
    status = WdfIoQueueCreate( device,
                               &ioQueueConfig,
                               &queueAttributes,
                               &queue );
    if (!NT_SUCCESS(status)) {
        return status;
    }
    //获取队列设备的扩展指针
    pQueueContext = QueueGetExtension(queue);
    //设置队列对象的扩展的设备对象扩展
    pQueueContext->DeviceExtension = pDeviceExtension;
    
    //设置进度条显示
    //status = SetForwardProgressOnQueue(queue);
    //if (!NT_SUCCESS(status)) {
    //    return status;
    //}
    
    //初始化设备扩展中一些变量
    pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
        (PWSTR) &pDeviceExtension->DriveLetterBuffer;
    pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
        sizeof(pDeviceExtension->DriveLetterBuffer);
    //从注册表中获取    这个注册表信息  其实是由inf文件指定
    RamDiskQueryDiskRegParameters(
        WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
        &pDeviceExtension->DiskRegInfo
        );
    //分配非分页内存给这个磁盘  DiskImage是磁盘镜像的意思
    //分页内存和非分页内存   分页内存的存储介质有可能在内存  也有可能在硬盘
    //非分页内存的存储介质一定是内存    所以分配非分页内存  不会引起复杂的换页操作和一些缺页中断
    //RAMDISK_TAG 代表空间标识  便于调试
    pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
        NonPagedPool,
        pDeviceExtension->DiskRegInfo.DiskSize,
        RAMDISK_TAG
        );
        
    if (pDeviceExtension->DiskImage) {

        UNICODE_STRING deviceName;
        UNICODE_STRING win32Name;
        //初始化磁盘空间
        RamDiskFormatDisk(pDeviceExtension);

        status = STATUS_SUCCESS;


        //    /DosDevice/xxx    windows下代表了磁盘设备
        RtlInitUnicodeString(&win32Name, DOS_DEVICE_NAME);
        RtlInitUnicodeString(&deviceName, NT_DEVICE_NAME);

        pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
            &pDeviceExtension->DosDeviceNameBuffer;
        pDeviceExtension->SymbolicLink.MaximumLength =
            sizeof(pDeviceExtension->DosDeviceNameBuffer);
        pDeviceExtension->SymbolicLink.Length = win32Name.Length;

        RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink, &win32Name);
        //初始化磁盘盘符
        RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
                                       &pDeviceExtension->DiskRegInfo.DriveLetter);

        status = WdfDeviceCreateSymbolicLink(device,
                                             &pDeviceExtension->SymbolicLink);
    }

    return status;
        
}

调用PAGED_CODE()保证代码可以调用非分页内存,这个宏其实是判断IRQL级别不处于DISPATCH_LEVEL级别及以上,如果是,则触发断言。

使用WdfDeviceInitAssignName初始化设备的名字,设备名和符号链接是不一样的,设备名只能在内核态被其他驱动识别。而符号链接则可以在用户态被应用程序识别。

使用WdfDeviceInitSetIoType指定设备的IO类型,一共有三种类型可选,1.WdfDeviceIoBuffered 使用缓冲区接受数据  2.WdfDeviceIoDirect,直接接收数据,IRP所携带的缓冲区 ,可以直接使用

3.WdfDeviceIoBufferedOrDirect 前面两种方式都会使用

使用函数WdfDeviceInitSetExclusive设置设备为非独占

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE指定设备扩展使用什么样的数据结构存储数据,DEVICE_EXTENSION为自定义的结构体

deviceAttributes.EvtCleanupCallback这里指定设备的清理函数为RamDiskEvtDeviceContextCleanup

准备完毕之后使用WdfDeviceCreate创建磁盘设备,参数都是前面处理过,其中device为传出参数

下面是创建队列对象的代码

ioQueueConfig为队列对象的配置对象,首先通过WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE初始化为默认值,然后指定IoDeviceControl和读写事件,通过WdfIoQueueCreate创建这个队列对象

注意的是队列对象的扩展结构里面包含了设备对象的扩展结构指针

RamDiskQueryDiskRegParameters取得注册表数据,系统在驱动加载之前,会首先提供访问注册表的功能,这里的数据其实是安装驱动时ramdisk.inf指定

pDeviceExtension->DiskImage这个指针是磁盘镜像,即磁盘位置的起始,然后为这个指针分配非分页内存,调用ExAllocatePoolWithTag传入第一个参数NonPagedPool。使用非分页内存的原因是,非分页内存的存储介质一定是内存,所以分配非分页内存 ,不会引起复杂的换页操作和一些缺页中断。

如果分配内存成功,则使用RamDiskFormatDisk函数初始化磁盘空间。接下来使用了win32Name存储符号链接,最终的符号链接为/DosDevice/Z: ,在用户态会默认显示Z作为盘符

刚开始我找到WdfControlFinishInitializing这个函数,以为可以通知用户层更新,从而显示出新建的磁盘对象,后来发现这个函数不能达到目的。在这里也求大神指教,如何不用重启,即可显示出新建的磁盘设备。

//初始化磁盘结构的一些数据
NTSTATUS
RamDiskFormatDisk(
    IN PDEVICE_EXTENSION devExt
    )

{
    //将分配的非分页内存的首地址  转化为DBR结构的指针
    PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
    //指向FAT表的指针
    PUCHAR       firstFatSector;
    //根目录入口点个数
    ULONG        rootDirEntries;
    //一个簇由多少扇区组成
    ULONG        sectorsPerCluster;
    //fat文件系统类型
    USHORT       fatType;        // Type FAT 12 or 16
    //FAT表里面有多少表项
    USHORT       fatEntries;     // Number of cluster entries in FAT
    //一个FAT表需要占多少簇存储
    USHORT       fatSectorCnt;   // Number of sectors for FAT
    //第一个根目录入口点
    PDIR_ENTRY   rootDir;        // Pointer to first entry in root dir
    //保证这个函数可以操作paged内存
    PAGED_CODE();
    
    ASSERT(sizeof(BOOT_SECTOR) == 512);
    ASSERT(devExt->DiskImage != NULL);
    //清空磁盘镜像
    RtlZeroMemory(devExt->DiskImage, devExt->DiskRegInfo.DiskSize);
    //每个扇区有512个字节
    devExt->DiskGeometry.BytesPerSector = 512;
    //每个磁道有32个扇区
    devExt->DiskGeometry.SectorsPerTrack = 32;     // Using Ramdisk value
    //每个柱面有两个磁道
    devExt->DiskGeometry.TracksPerCylinder = 2;    // Using Ramdisk value
    //计算得出磁柱面数
    devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / 512 / 32 / 2;
    devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;
    KdPrint((
        "Cylinders: %ld
 TracksPerCylinder: %ld
 SectorsPerTrack: %ld
 BytesPerSector: %ld
",
        devExt->DiskGeometry.Cylinders.QuadPart, devExt->DiskGeometry.TracksPerCylinder,
        devExt->DiskGeometry.SectorsPerTrack, devExt->DiskGeometry.BytesPerSector
        ));
    //初始化根目录入口点个数
    rootDirEntries = devExt->DiskRegInfo.RootDirEntries;
    //一个簇由多少扇区组成
    sectorsPerCluster = devExt->DiskRegInfo.SectorsPerCluster;
    //这里  调整根目录数目的地方很疑惑   
    if (rootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1)) {

        rootDirEntries =
            (rootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
                ~ (DIR_ENTRIES_PER_SECTOR - 1);
    }

    KdPrint((
        "Root dir entries: %ld
 Sectors/cluster: %ld
",
        rootDirEntries, sectorsPerCluster
        ));
    //硬编码写入跳转指令
    bootSector->bsJump[0] = 0xeb;
    bootSector->bsJump[1] = 0x3c;
    bootSector->bsJump[2] = 0x90;
    //oem名字
        bootSector->bsOemName[0] = 'R';
    bootSector->bsOemName[1] = 'a';
    bootSector->bsOemName[2] = 'j';
    bootSector->bsOemName[3] = 'u';
    bootSector->bsOemName[4] = 'R';
    bootSector->bsOemName[5] = 'a';
    bootSector->bsOemName[6] = 'm';
    bootSector->bsOemName[7] = ' ';
    //每个扇区有多少字节
    bootSector->bsBytesPerSec = (SHORT)devExt->DiskGeometry.BytesPerSector;
    //指定这个磁盘卷保留扇区   仅DBR这一个扇区为保留扇区
    bootSector->bsResSectors  = 1;
    //fat表一般一式两份  但这里就创建一份就可以
    bootSector->bsFATs        = 1;
    //指定根目录入口点个数
     bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
     //磁盘总扇区数   磁盘大小除以扇区大小
    bootSector->bsSectors     = (USHORT)(devExt->DiskRegInfo.DiskSize /
                                         devExt->DiskGeometry.BytesPerSector);
    //磁盘介质类型
    bootSector->bsMedia       = (UCHAR)devExt->DiskGeometry.MediaType;
    //每个簇有多少个扇区
    bootSector->bsSecPerClus  = (UCHAR)sectorsPerCluster;
    //fat表项数  总扇区数-保留扇区数
    //这里很疑惑   如何得到FAT表项数
    fatEntries =
        (bootSector->bsSectors - bootSector->bsResSectors -
            bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
                bootSector->bsSecPerClus + 2;
                
                
    //如果表项数  大于2的12次方   则使用FAT16
    if (fatEntries > 4087) {
        fatType =  16;
        //这一步的调整  我很不理解
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (fatEntries * 2 + 511) / 512;
    }
    else {
        fatType =  12;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
        fatEntries   = fatEntries + fatSectorCnt;
        fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
    }
    //初始化FAT表所占的扇区数
    bootSector->bsFATsecs       = fatSectorCnt;
    //初始化DBR每个磁道的扇区数
    bootSector->bsSecPerTrack   = (USHORT)devExt->DiskGeometry.SectorsPerTrack;
    //初始化每个柱面的磁道数
    bootSector->bsHeads         = (USHORT)devExt->DiskGeometry.TracksPerCylinder;
    //初始化启动签名  windows要求必须是0x29或者0x28
    bootSector->bsBootSignature = 0x29;
    //卷ID  随便填写
    bootSector->bsVolumeID      = 0x12345678;
    //初始化卷标
        bootSector->bsLabel[0]  = 'R';
    bootSector->bsLabel[1]  = 'a';
    bootSector->bsLabel[2]  = 'm';
    bootSector->bsLabel[3]  = 'D';
    bootSector->bsLabel[4]  = 'i';
    bootSector->bsLabel[5]  = 's';
    bootSector->bsLabel[6]  = 'k';
    bootSector->bsLabel[7]  = ' ';
    bootSector->bsLabel[8]  = ' ';
    bootSector->bsLabel[9]  = ' ';
    bootSector->bsLabel[10] = ' ';
    //设置磁盘文件类型
    bootSector->bsFileSystemType[0] = 'F';
    bootSector->bsFileSystemType[1] = 'A';
    bootSector->bsFileSystemType[2] = 'T';
    bootSector->bsFileSystemType[3] = '1';
    bootSector->bsFileSystemType[4] = '?';
    bootSector->bsFileSystemType[5] = ' ';
    bootSector->bsFileSystemType[6] = ' ';
    bootSector->bsFileSystemType[7] = ' ';

    bootSector->bsFileSystemType[4] = ( fatType == 16 ) ? '6' : '2';
    //设置DBR结束标识
    bootSector->bsSig2[0] = 0x55;
    bootSector->bsSig2[1] = 0xAA;
    //初始化FAT表结构
    firstFatSector    = (PUCHAR)(bootSector + 1);
    firstFatSector[0] = (UCHAR)devExt->DiskGeometry.MediaType;
    firstFatSector[1] = 0xFF;
    firstFatSector[2] = 0xFF;

    if (fatType == 16) {
        firstFatSector[3] = 0xFF;
    }
    
    
     rootDir = (PDIR_ENTRY)(bootSector + 1 + fatSectorCnt);
     
    //接下来 初始化根目录入口点信息
    rootDir->deName[0] = 'M';
    rootDir->deName[1] = 'S';
    rootDir->deName[2] = '-';
    rootDir->deName[3] = 'R';
    rootDir->deName[4] = 'A';
    rootDir->deName[5] = 'M';
    rootDir->deName[6] = 'D';
    rootDir->deName[7] = 'R';

    //
    // Set device extension name to "IVE"
    // NOTE: Fill all 3 characters, eg. sizeof(rootDir->deExtension);
    //
    rootDir->deExtension[0] = 'I';
    rootDir->deExtension[1] = 'V';
    rootDir->deExtension[2] = 'E';

    rootDir->deAttributes = DIR_ATTR_VOLUME;

    return STATUS_SUCCESS;

}

RamDiskFormatDisk函数用来初始化硬盘空间,如果是引导盘,那么硬盘镜像的第一个结构是MBR(主引导扇区),但是ramdisk虚拟的硬盘没有引导系统的作用,所以,第一个结构是DBR,结构如下

typedef struct  _BOOT_SECTOR
{
    UCHAR       bsJump[3];          // x86 jmp instruction, checked by FS
    CCHAR       bsOemName[8];       // OEM name of formatter
    USHORT      bsBytesPerSec;      // Bytes per Sector
    UCHAR       bsSecPerClus;       // Sectors per Cluster
    USHORT      bsResSectors;       // Reserved Sectors
    UCHAR       bsFATs;             // Number of FATs - we always use 1
    USHORT      bsRootDirEnts;      // Number of Root Dir Entries
    USHORT      bsSectors;          // Number of Sectors
    UCHAR       bsMedia;            // Media type - we use RAMDISK_MEDIA_TYPE
    USHORT      bsFATsecs;          // Number of FAT sectors
    USHORT      bsSecPerTrack;      // Sectors per Track - we use 32
    USHORT      bsHeads;            // Number of Heads - we use 2
    ULONG       bsHiddenSecs;       // Hidden Sectors - we set to 0
    ULONG       bsHugeSectors;      // Number of Sectors if > 32 MB size
    UCHAR       bsDriveNumber;      // Drive Number - not used
    UCHAR       bsReserved1;        // Reserved
    UCHAR       bsBootSignature;    // New Format Boot Signature - 0x29
    ULONG       bsVolumeID;         // VolumeID - set to 0x12345678
    CCHAR       bsLabel[11];        // Label - set to RamDisk
    CCHAR       bsFileSystemType[8];// File System Type - FAT12 or FAT16
    CCHAR       bsReserved2[448];   // Reserved
    UCHAR       bsSig2[2];          // Originial Boot Signature - 0x55, 0xAA
}   BOOT_SECTOR, *PBOOT_SECTOR;

DBR结构如下图

原文地址:https://www.cnblogs.com/zwt1234/p/4521697.html