windows-nt/Source/XPSP1/NT/base/xip/xipdisk/xipdisk.c

715 lines
19 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
XIPDisk.c
Abstract:
This is the XIP Disk driver for Whistler NT/Embedded.
Author:
DavePr 18-Sep-2000 -- base one NT4 DDK ramdisk by RobertN 10-Mar-1993.
Environment:
Kernel mode only.
Notes:
Revision History:
--*/
//
// Include files.
//
#include <ntddk.h>
#include "initguid.h"
#include "mountdev.h"
#include <ntdddisk.h> // Disk device IOCTLs, DiskClassGuid
#include "fat.h"
#include "xip.h"
#include "XIPDisk.h"
//
// ISSUE-2000/10/11-DavePr -- haven't decided how to define DO_XIP appropriately.
//
#ifndef DO_XIP
#define DO_XIP 0x00020000
#endif
#include <string.h>
NTSTATUS
XIPDiskCreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system when the XIPDisk is opened or
closed.
No action is performed other than completing the request successfully.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_INVALID_PARAMETER if parameters are invalid,
STATUS_SUCCESS otherwise.
--*/
{
PXIPDISK_EXTENSION diskExtension = NULL; // ptr to device extension
PBIOS_PARAMETER_BLOCK bios;
NTSTATUS status;
diskExtension = DeviceObject->DeviceExtension;
status = XIPDispatch(XIPCMD_NOOP, NULL, 0);
if (!NT_SUCCESS(status) || !diskExtension->BootParameters.BasePage) {
status = STATUS_DEVICE_NOT_READY;
} else {
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
}
NTSTATUS
XIPDiskReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to read or write to a
device that we control.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_INVALID_PARAMETER if parameters are invalid,
STATUS_SUCCESS otherwise.
--*/
{
PXIPDISK_EXTENSION diskExtension;
PIO_STACK_LOCATION irpSp;
PUCHAR bufferAddress, diskByteAddress;
PUCHAR romPageAddress = NULL;
ULONG_PTR ioOffset;
ULONG ioLength;
NTSTATUS status;
PHYSICAL_ADDRESS physicalAddress;
ULONG mappingSize;
//
// Set up necessary object and extension pointers.
//
diskExtension = DeviceObject->DeviceExtension;
irpSp = IoGetCurrentIrpStackLocation(Irp);
//
// Check for invalid parameters. It is an error for the starting offset
// + length to go past the end of the buffer, or for the offset or length
// not to be a proper multiple of the sector size.
//
// Others are possible, but we don't check them since we trust the
// file system and they aren't deadly.
//
if (irpSp->Parameters.Read.ByteOffset.HighPart) {
status = STATUS_INVALID_PARAMETER;
goto done;
}
ioOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
ioLength = irpSp->Parameters.Read.Length;
if (ioLength == 0) {
status = STATUS_SUCCESS;
goto done;
}
if (ioOffset + ioLength < ioOffset) {
status = STATUS_INVALID_PARAMETER;
goto done;
}
if ((ioOffset | ioLength) & (diskExtension->BiosParameters.BytesPerSector - 1)) {
status = STATUS_INVALID_PARAMETER;
goto done;
}
if ((ioOffset + ioLength) > (diskExtension->BootParameters.PageCount * PAGE_SIZE)) {
status = STATUS_NONEXISTENT_SECTOR;
goto done;
}
if (irpSp->MajorFunction == IRP_MJ_WRITE && diskExtension->BootParameters.ReadOnly) {
status = STATUS_MEDIA_WRITE_PROTECTED;
goto done;
}
//
// Map the pages in the ROM into system space
//
mappingSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES (ioOffset, ioLength) * PAGE_SIZE;
//
// Get a system-space pointer to the disk region.
//
physicalAddress.QuadPart = (diskExtension->BootParameters.BasePage + (ioOffset/PAGE_SIZE)) * PAGE_SIZE;
romPageAddress = MmMapIoSpace(physicalAddress, mappingSize, MmCached);
if (! romPageAddress) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
diskByteAddress = romPageAddress + (ioOffset & (PAGE_SIZE-1));
//
// Get a system-space pointer to the user's buffer. A system
// address must be used because we may already have left the
// original caller's address space.
//
Irp->IoStatus.Information = irpSp->Parameters.Read.Length;
ASSERT (Irp->MdlAddress != NULL);
bufferAddress = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority );
if (! bufferAddress) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
status = STATUS_SUCCESS;
switch (irpSp->MajorFunction) {
case IRP_MJ_READ:
RtlCopyMemory( bufferAddress, diskByteAddress, ioLength );
break;
case IRP_MJ_WRITE:
RtlCopyMemory( diskByteAddress, bufferAddress, ioLength );
break;
default:
ASSERT(FALSE);
status = STATUS_INVALID_PARAMETER;
}
done:
if (romPageAddress) {
MmUnmapIoSpace (romPageAddress, mappingSize);
}
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
}
NTSTATUS
XIPDiskDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to perform a device I/O
control function.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_SUCCESS if recognized I/O control code,
STATUS_INVALID_DEVICE_REQUEST otherwise.
--*/
{
PBIOS_PARAMETER_BLOCK bios;
PXIPDISK_EXTENSION diskExtension;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
ULONG info;
//
// Set up necessary object and extension pointers.
//
diskExtension = DeviceObject->DeviceExtension;
bios = &diskExtension->BiosParameters;
irpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Assume failure.
//
status = STATUS_INVALID_DEVICE_REQUEST;
info = 0;
//
// Determine which I/O control code was specified.
//
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_DISK_GET_MEDIA_TYPES:
case IOCTL_STORAGE_GET_MEDIA_TYPES:
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
//
// Return the drive geometry for the virtual disk. Note that
// we return values which were made up to suit the disk size.
//
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {
status = STATUS_INVALID_PARAMETER;
} else {
PDISK_GEOMETRY outputBuffer;
outputBuffer = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
outputBuffer->MediaType = FixedMedia;
outputBuffer->Cylinders.QuadPart = diskExtension->NumberOfCylinders;
outputBuffer->TracksPerCylinder = diskExtension->TracksPerCylinder;
outputBuffer->SectorsPerTrack = bios->SectorsPerTrack;
outputBuffer->BytesPerSector = bios->BytesPerSector;
status = STATUS_SUCCESS;
info = sizeof( DISK_GEOMETRY );
}
break;
#if 0
//
// Ignore these IOCTLs for now.
//
case IOCTL_DISK_SET_PARTITION_INFO:
case IOCTL_DISK_SET_DRIVE_LAYOUT:
status = STATUS_SUCCESS;
break;
#endif
case IOCTL_DISK_GET_PARTITION_INFO:
//
// Return the information about the partition.
//
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARTITION_INFORMATION)) {
status = STATUS_INVALID_PARAMETER;
} else {
PPARTITION_INFORMATION outputBuffer;
outputBuffer = (PPARTITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
//
// Fat hardwired here...
//
outputBuffer->PartitionType = PARTITION_FAT_16;
outputBuffer->BootIndicator = diskExtension->BootParameters.SystemDrive;
outputBuffer->RecognizedPartition = TRUE;
outputBuffer->RewritePartition = FALSE;
outputBuffer->StartingOffset.QuadPart = 0;
outputBuffer->PartitionLength.QuadPart = diskExtension->BootParameters.PageCount * PAGE_SIZE;
outputBuffer->HiddenSectors = diskExtension->BiosParameters.HiddenSectors;
status = STATUS_SUCCESS;
info = sizeof(PARTITION_INFORMATION);
}
break;
case IOCTL_DISK_VERIFY:
{
PVERIFY_INFORMATION verifyInformation;
ULONG buflen;
ULONG_PTR ioOffset;
ULONG ioLength;
buflen = irpSp->Parameters.DeviceIoControl.InputBufferLength;
if ( buflen < sizeof(VERIFY_INFORMATION) ) {
status = STATUS_INVALID_PARAMETER;
break;
}
verifyInformation = Irp->AssociatedIrp.SystemBuffer;
if (verifyInformation->StartingOffset.HighPart) {
status = STATUS_DISK_CORRUPT_ERROR;
break;
}
ioOffset = verifyInformation->StartingOffset.LowPart;
ioLength = verifyInformation->Length;
if (ioLength == 0) {
status = STATUS_SUCCESS;
} else if ((ioOffset | ioLength) & (diskExtension->BiosParameters.BytesPerSector - 1)) {
status = STATUS_INVALID_PARAMETER;
} else if ((ioOffset + ioLength) > (diskExtension->BootParameters.PageCount * PAGE_SIZE)) {
status = STATUS_NONEXISTENT_SECTOR;
} else {
status = STATUS_SUCCESS;
}
break;
}
case IOCTL_DISK_IS_WRITABLE:
status = diskExtension->BootParameters.ReadOnly? STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS;
break;
case IOCTL_DISK_CHECK_VERIFY:
case IOCTL_STORAGE_CHECK_VERIFY:
case IOCTL_STORAGE_CHECK_VERIFY2:
status = STATUS_SUCCESS;
break;
default:
//
// The specified I/O control code is unrecognized by this driver.
// The I/O status field in the IRP has already been set so just
// terminate the switch.
//
#if DBG
DbgPrint("XIPDisk: ERROR: unrecognized IOCTL %x\n",
irpSp->Parameters.DeviceIoControl.IoControlCode);
#endif
break;
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
{
PMOUNTDEV_NAME mountName;
ULONG outlen;
outlen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
if ( outlen < sizeof(MOUNTDEV_NAME) ) {
status = STATUS_INVALID_PARAMETER;
break;
}
mountName = Irp->AssociatedIrp.SystemBuffer;
mountName->NameLength = diskExtension->DeviceName.Length;
if ( outlen < mountName->NameLength + sizeof(WCHAR)) {
status = STATUS_BUFFER_OVERFLOW;
info = sizeof(MOUNTDEV_NAME);
break;
}
RtlCopyMemory( mountName->Name, diskExtension->DeviceName.Buffer, mountName->NameLength);
status = STATUS_SUCCESS;
info = mountName->NameLength + sizeof(WCHAR);
break;
}
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
{
PMOUNTDEV_UNIQUE_ID uniqueId;
ULONG outlen;
outlen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
if (outlen < sizeof(MOUNTDEV_UNIQUE_ID)) {
status = STATUS_INVALID_PARAMETER;
break;
}
uniqueId = Irp->AssociatedIrp.SystemBuffer;
uniqueId->UniqueIdLength = sizeof(XIPDISK_DEVICENAME);
if (outlen < uniqueId->UniqueIdLength) {
status = STATUS_BUFFER_OVERFLOW;
info = sizeof(MOUNTDEV_UNIQUE_ID);
break;
}
RtlCopyMemory( uniqueId->UniqueId, XIPDISK_DEVICENAME, uniqueId->UniqueIdLength );
status = STATUS_SUCCESS;
info = uniqueId->UniqueIdLength;
break;
}
case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME:
{
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
//
// Finish the I/O operation by simply completing the packet and returning
// the same status as in the packet itself.
// Note that IoCompleteRequest may deallocate Irp before returning.
//
Irp->IoStatus.Information = info;
Irp->IoStatus.Status = status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
}
VOID
XIPDiskUnloadDriver(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine is called by the I/O system to unload the driver.
Any resources previously allocated must be freed.
Arguments:
DriverObject - a pointer to the object that represents our driver.
Return Value:
None
--*/
{
PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
PXIPDISK_EXTENSION diskExtension = deviceObject->DeviceExtension;
RtlFreeUnicodeString(&diskExtension->InterfaceString);
diskExtension->InterfaceString.Buffer = NULL;
if (deviceObject != NULL) {
IoDeleteDevice( deviceObject );
}
}
NTSTATUS
DriverEntry(
IN OUT PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called by the Operating System to initialize the driver.
Arguments:
DriverObject - a pointer to a device extension object for the XIPDisk driver.
RegistryPath - a pointer to our Services key in the registry.
Return Value:
STATUS_SUCCESS if this disk is initialized; an error otherwise.
--*/
{
XIP_BOOT_PARAMETERS xipbootparameters;
PBIOS_PARAMETER_BLOCK bios;
NTSTATUS status;
// UNICODE_STRING deviceName;
UNICODE_STRING realDeviceName;
UNICODE_STRING dosSymlink;
UNICODE_STRING driveLetter;
PDEVICE_OBJECT pdo = NULL;
PDEVICE_OBJECT deviceObject;
PXIPDISK_EXTENSION ext = NULL; // ptr to device extension
//
// Read the parameters from the registry
//
status = XIPDispatch(XIPCMD_GETBOOTPARAMETERS, &xipbootparameters, sizeof(xipbootparameters));
if (!NT_SUCCESS(status)) {
return status;
}
if (xipbootparameters.BasePage == 0) {
return STATUS_NO_SUCH_DEVICE;
}
// Initialize the driver object with this driver's entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = XIPDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = XIPDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = XIPDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = XIPDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = XIPDiskDeviceControl;
//
// Create and initialize a device object for the disk.
//
ObReferenceObject(DriverObject);
status = IoReportDetectedDevice(
DriverObject,
InterfaceTypeUndefined,
-1,
-1,
NULL,
NULL,
TRUE,
&pdo
);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Create the XIP root device.
//
RtlInitUnicodeString(&realDeviceName, XIPDISK_DEVICENAME);
status = IoCreateDevice( DriverObject,
sizeof( XIPDISK_EXTENSION ),
&realDeviceName,
FILE_DEVICE_VIRTUAL_DISK,
0,
FALSE,
&deviceObject );
if (!NT_SUCCESS(status)) {
return status;
}
// ISSUE-2000/10/14-DavePr -- Hardwiring the driveLetter because I haven't
// figured out how to get the mountmgr to give out a drive letter. Naming
// it as a form of floppy (deviceName) was one suggestion that failed (so far).
// The dosSymlink isn't really necessary, but is another
//
// Create symbolic links. Ignore failures
//
// RtlInitUnicodeString(&deviceName, XIPDISK_FLOPPYNAME);
RtlInitUnicodeString(&dosSymlink, XIPDISK_DOSNAME);
RtlInitUnicodeString(&driveLetter, XIPDISK_DRIVELETTER);
// (void) IoCreateSymbolicLink(&deviceName, &realDeviceName);
(void) IoCreateSymbolicLink(&dosSymlink, &realDeviceName);
(void) IoCreateSymbolicLink(&driveLetter, &realDeviceName);
//
// Initialize device object and extension.
//
deviceObject->Flags |= DO_DIRECT_IO | DO_XIP;
deviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
ext = deviceObject->DeviceExtension;
bios = &ext->BiosParameters;
//
// Initialize the newly allocated disk extension from our temporary
// Get the bios boot parameters from the kernel
//
ext->BootParameters = xipbootparameters;
status = XIPDispatch(XIPCMD_GETBIOSPARAMETERS, bios, sizeof(*bios));
//
// Fill in the device objects
//
ext->DeviceObject = deviceObject;
// ext->DeviceName = deviceName;
ext->DeviceName = realDeviceName;
ext->TracksPerCylinder = 1;
ext->BytesPerCylinder = bios->BytesPerSector * bios->SectorsPerTrack * ext->TracksPerCylinder;
ext->NumberOfCylinders = ext->BootParameters.PageCount * PAGE_SIZE / ext->BytesPerCylinder;
//
// Attach the root device
//
ext->TargetObject = IoAttachDeviceToDeviceStack(deviceObject, pdo);
if (!ext->TargetObject) {
// IoDeleteSymbolicLink(&deviceName);
IoDeleteSymbolicLink(&dosSymlink);
IoDeleteSymbolicLink(&driveLetter);
IoDeleteSymbolicLink(&realDeviceName);
IoDeleteDevice(deviceObject);
return STATUS_NO_SUCH_DEVICE;
}
ext->UnderlyingPDO = pdo;
status = IoRegisterDeviceInterface(pdo,
(LPGUID)&DiskClassGuid,
NULL,
&ext->InterfaceString );
if (NT_SUCCESS(status)) {
status = IoSetDeviceInterfaceState( &ext->InterfaceString, TRUE );
if (!NT_SUCCESS(status)) {
DbgPrint("XIP: Warning: ignored failure %x retruned by IoSetDeviceInterface\n", status);
}
} else {
DbgPrint("XIP: Warning: ignored failure %x retruned by IoRegisterDeviceInterface\n", status);
}
return STATUS_SUCCESS;
}