/*++ Copyright (c) 2012 Minoca Corp. All Rights Reserved Module Name: ramdisk.c Abstract: This module implements driver support for RAM disks. This RAM disk driver serves as an excellent simple example for Open, Close, I/O, and system control IRPs. It is fairly unusual (and therefore probably not a good example) in relation to its DriverEntry, AddDevice, and StateChange handling. Be aware of this if using this driver as a template to write your own. Author: Evan Green 17-Oct-2012 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include // // ---------------------------------------------------------------- Definitions // #define RAM_DISK_ALLOCATION_TAG 0x444D4152 // 'DMAR' #define RAM_DISK_SECTOR_SIZE 0x200 // // --------------------------------------------------------------------- Macros // // // ------------------------------------------------------ Data Type Definitions // /*++ Structure Description: This structure defines state associated with a RAM disk. Members: PhysicalAddress - Stores the physical address of the buffer. Buffer - Stores a pointer to the buffer of the raw RAM disk. Size - Stores the total size of the RAM disk, in bytes. --*/ typedef struct _RAM_DISK_DEVICE { PHYSICAL_ADDRESS PhysicalAddress; PVOID Buffer; ULONGLONG Size; } RAM_DISK_DEVICE, *PRAM_DISK_DEVICE; // // ----------------------------------------------- Internal Function Prototypes // KSTATUS RamDiskAddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ); VOID RamDiskDispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID RamDiskDispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID RamDiskDispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID RamDiskDispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID RamDiskDispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); // // -------------------------------------------------------------------- Globals // // // Store a pointer to the driver object. // PDRIVER RamDiskDriver = NULL; // // Store the next identifier. // volatile ULONG RamDiskNextIdentifier = 0; // // ------------------------------------------------------------------ Functions // KSTATUS DriverEntry ( PDRIVER Driver ) /*++ Routine Description: This routine is the entry point for the RAM disk driver. It registers its other dispatch functions, and performs driver-wide initialization. Arguments: Driver - Supplies a pointer to the driver object. Return Value: STATUS_SUCCESS on success. Failure code on error. --*/ { volatile ULONG DeviceId; CHAR DeviceIdString[11]; DRIVER_FUNCTION_TABLE FunctionTable; PSYSTEM_RESOURCE_HEADER GenericHeader; PRAM_DISK_DEVICE RamDiskDevice; PSYSTEM_RESOURCE_RAM_DISK RamDiskResource; KSTATUS Status; RamDiskDriver = Driver; RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE)); FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION; FunctionTable.AddDevice = RamDiskAddDevice; FunctionTable.DispatchStateChange = RamDiskDispatchStateChange; FunctionTable.DispatchOpen = RamDiskDispatchOpen; FunctionTable.DispatchClose = RamDiskDispatchClose; FunctionTable.DispatchIo = RamDiskDispatchIo; FunctionTable.DispatchSystemControl = RamDiskDispatchSystemControl; Status = IoRegisterDriverFunctions(Driver, &FunctionTable); if (!KSUCCESS(Status)) { goto DriverEntryEnd; } // // Get all RAM disks from the boot environment. This is not normally how // devices are created or enumerated. The RAM disk is special in that its // devices and resources are essentially born out of the boot environment. // Don't copy this loop if using this driver as a template. // while (TRUE) { GenericHeader = KeAcquireSystemResource(SystemResourceRamDisk); if (GenericHeader == NULL) { break; } RamDiskResource = (PSYSTEM_RESOURCE_RAM_DISK)GenericHeader; // // Allocate the internal data structure. // RamDiskDevice = MmAllocateNonPagedPool(sizeof(RAM_DISK_DEVICE), RAM_DISK_ALLOCATION_TAG); if (RamDiskDevice == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto DriverEntryEnd; } RtlZeroMemory(RamDiskDevice, sizeof(RAM_DISK_DEVICE)); RamDiskDevice->PhysicalAddress = RamDiskResource->Header.PhysicalAddress; RamDiskDevice->Buffer = RamDiskResource->Header.VirtualAddress; RamDiskDevice->Size = RamDiskResource->Header.Size; DeviceId = RtlAtomicAdd32(&RamDiskNextIdentifier, 1); RtlPrintToString(DeviceIdString, 11, CharacterEncodingDefault, "RamDisk%x", DeviceId); // // Create the RAM disk device. // Status = IoCreateDevice(RamDiskDriver, RamDiskDevice, NULL, DeviceIdString, DISK_CLASS_ID, NULL, NULL); if (!KSUCCESS(Status)) { goto DriverEntryEnd; } } DriverEntryEnd: return Status; } KSTATUS RamDiskAddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ) /*++ Routine Description: This routine is called when a RAM disk is detected. Arguments: Driver - Supplies a pointer to the driver being called. DeviceId - Supplies a pointer to a string with the device ID. ClassId - Supplies a pointer to a string containing the device's class ID. CompatibleIds - Supplies a pointer to a string containing device IDs that would be compatible with this device. DeviceToken - Supplies an opaque token that the driver can use to identify the device in the system. This token should be used when attaching to the stack. Return Value: STATUS_SUCCESS on success. Failure code if the driver was unsuccessful in attaching itself. --*/ { // // The RAM disk is not a real device, so it is not expected to be // attaching to emerging stacks. A proper driver should examine the device // ID and call IoAttachDriverToDevice to connect their driver to a newly // enumerated device. // return STATUS_NOT_IMPLEMENTED; } VOID RamDiskDispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { BOOL CompleteIrp; KSTATUS Status; ASSERT(Irp->MajorCode == IrpMajorStateChange); // // The IRP is on its way down the stack. Do most processing here. // if (Irp->Direction == IrpDown) { Status = STATUS_NOT_SUPPORTED; CompleteIrp = TRUE; switch (Irp->MinorCode) { case IrpMinorQueryResources: Status = STATUS_SUCCESS; break; case IrpMinorStartDevice: Status = STATUS_SUCCESS; break; case IrpMinorQueryChildren: Irp->U.QueryChildren.Children = NULL; Irp->U.QueryChildren.ChildCount = 0; Status = STATUS_SUCCESS; break; // // Pass all other IRPs down. // default: CompleteIrp = FALSE; break; } // // Complete the IRP unless there's a reason not to. Normal drivers // should only complete the IRP if they're a bus driver or an error // occurred. The RAM disk is special as it created itself (and so it is // its own bus driver). // if (CompleteIrp != FALSE) { IoCompleteIrp(RamDiskDriver, Irp, Status); } // // The IRP is completed and is on its way back up. In normal device // drivers, this would be where to process the IRP, as by this point the // bus driver has performed necessary work (like enabling access to the // device on the bus in the case of start IRPs). // } else { ASSERT(Irp->Direction == IrpUp); } return; } VOID RamDiskDispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Open IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PRAM_DISK_DEVICE Disk; PRAM_DISK_DEVICE DiskCopy; Disk = (PRAM_DISK_DEVICE)DeviceContext; DiskCopy = MmAllocatePagedPool(sizeof(RAM_DISK_DEVICE), RAM_DISK_ALLOCATION_TAG); if (DiskCopy == NULL) { IoCompleteIrp(RamDiskDriver, Irp, STATUS_INSUFFICIENT_RESOURCES); return; } RtlCopyMemory(DiskCopy, Disk, sizeof(RAM_DISK_DEVICE)); Irp->U.Open.DeviceContext = DiskCopy; IoCompleteIrp(RamDiskDriver, Irp, STATUS_SUCCESS); return; } VOID RamDiskDispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Close IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { MmFreePagedPool(Irp->U.Close.DeviceContext); IoCompleteIrp(RamDiskDriver, Irp, STATUS_SUCCESS); return; } VOID RamDiskDispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles I/O IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { UINTN BytesToComplete; KSTATUS CompletionStatus; PRAM_DISK_DEVICE Disk; IO_OFFSET IoOffset; ULONG IrpReadWriteFlags; BOOL ReadWriteIrpPrepared; KSTATUS Status; BOOL ToIoBuffer; ASSERT(Irp->Direction == IrpDown); Disk = Irp->U.ReadWrite.DeviceContext; ReadWriteIrpPrepared = FALSE; ASSERT(IS_ALIGNED(Irp->U.ReadWrite.IoOffset, RAM_DISK_SECTOR_SIZE)); ASSERT(IS_ALIGNED(Irp->U.ReadWrite.IoSizeInBytes, RAM_DISK_SECTOR_SIZE)); ASSERT(Irp->U.ReadWrite.IoBuffer != NULL); Irp->U.ReadWrite.IoBytesCompleted = 0; IoOffset = Irp->U.ReadWrite.IoOffset; if (IoOffset >= Disk->Size) { Status = STATUS_OUT_OF_BOUNDS; goto DispatchIoEnd; } BytesToComplete = Irp->U.ReadWrite.IoSizeInBytes; if ((IoOffset + BytesToComplete) > Disk->Size) { BytesToComplete = Disk->Size - Irp->U.ReadWrite.IoOffset; } ToIoBuffer = TRUE; IrpReadWriteFlags = IRP_READ_WRITE_FLAG_POLLED; if (Irp->MinorCode == IrpMinorIoWrite) { ToIoBuffer = FALSE; IrpReadWriteFlags |= IRP_READ_WRITE_FLAG_WRITE; } // // Prepare the I/O buffer for polled I/O. // Status = IoPrepareReadWriteIrp(&(Irp->U.ReadWrite), 1, 0, MAX_ULONGLONG, IrpReadWriteFlags); if (!KSUCCESS(Status)) { goto DispatchIoEnd; } ReadWriteIrpPrepared = TRUE; // // Transfer the data between the disk and I/O buffer. // Status = MmCopyIoBufferData(Irp->U.ReadWrite.IoBuffer, (PUCHAR)Disk->Buffer + IoOffset, 0, BytesToComplete, ToIoBuffer); if (!KSUCCESS(Status)) { goto DispatchIoEnd; } Irp->U.ReadWrite.IoBytesCompleted = BytesToComplete; DispatchIoEnd: if (ReadWriteIrpPrepared != FALSE) { CompletionStatus = IoCompleteReadWriteIrp(&(Irp->U.ReadWrite), IrpReadWriteFlags); if (!KSUCCESS(CompletionStatus) && KSUCCESS(Status)) { Status = CompletionStatus; } } Irp->U.ReadWrite.NewIoOffset = IoOffset + Irp->U.ReadWrite.IoBytesCompleted; IoCompleteIrp(RamDiskDriver, Irp, Status); return; } VOID RamDiskDispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles System Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PVOID Context; PRAM_DISK_DEVICE Disk; PSYSTEM_CONTROL_FILE_OPERATION FileOperation; PSYSTEM_CONTROL_LOOKUP Lookup; PFILE_PROPERTIES Properties; ULONGLONG PropertiesFileSize; KSTATUS Status; Context = Irp->U.SystemControl.SystemContext; Disk = DeviceContext; switch (Irp->MinorCode) { case IrpMinorSystemControlLookup: Lookup = (PSYSTEM_CONTROL_LOOKUP)Context; Status = STATUS_PATH_NOT_FOUND; if (Lookup->Root != FALSE) { // // Enable opening of the root as a single file. // Properties = &(Lookup->Properties); Properties->FileId = 0; Properties->Type = IoObjectBlockDevice; Properties->HardLinkCount = 1; Properties->BlockSize = RAM_DISK_SECTOR_SIZE; Properties->BlockCount = Disk->Size / RAM_DISK_SECTOR_SIZE; WRITE_INT64_SYNC(&(Properties->FileSize), Disk->Size); Lookup->Flags = LOOKUP_FLAG_NON_CACHED; Status = STATUS_SUCCESS; } IoCompleteIrp(RamDiskDriver, Irp, Status); break; // // Writes to the disk's properties are not allowed. Fail if the data // has changed. // case IrpMinorSystemControlWriteFileProperties: FileOperation = (PSYSTEM_CONTROL_FILE_OPERATION)Context; Properties = FileOperation->FileProperties; READ_INT64_SYNC(&(Properties->FileSize), &PropertiesFileSize); if ((Properties->FileId != 0) || (Properties->Type != IoObjectBlockDevice) || (Properties->HardLinkCount != 1) || (Properties->BlockSize != RAM_DISK_SECTOR_SIZE) || (Properties->BlockCount != (Disk->Size / RAM_DISK_SECTOR_SIZE)) || (PropertiesFileSize != Disk->Size)) { Status = STATUS_NOT_SUPPORTED; } else { Status = STATUS_SUCCESS; } IoCompleteIrp(RamDiskDriver, Irp, Status); break; // // Do not support ramdisk device truncation. // case IrpMinorSystemControlTruncate: IoCompleteIrp(RamDiskDriver, Irp, STATUS_NOT_SUPPORTED); break; // // Gather and return device information. // case IrpMinorSystemControlDeviceInformation: IoCompleteIrp(RamDiskDriver, Irp, STATUS_NOT_SUPPORTED); break; case IrpMinorSystemControlSynchronize: IoCompleteIrp(RamDiskDriver, Irp, STATUS_SUCCESS); break; // // Ignore everything unrecognized. // default: ASSERT(FALSE); break; } return; } // // --------------------------------------------------------- Internal Functions //