2153 lines
56 KiB
C
2153 lines
56 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
simbad.c
|
||
|
||
Abstract:
|
||
|
||
This driver injects faults by maintaining an array of simulated
|
||
bad blocks and the error code to be returned. Each IO request
|
||
passing through SimBad is tested to see if any of the sectors
|
||
are in the array. If so, the request is failed with the appropriate
|
||
status.
|
||
|
||
Author:
|
||
|
||
Bob Rinne (bobri)
|
||
Mike Glass (mglass)
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
22nd June 94 -Venkat Added /b(BugCheck) and /n(RandomWriteDrop) feature
|
||
22nd Nov. 94 -KPeery Added /t(Resest) feature for restarts
|
||
23rd Mar. 95 -KPeery fixed resest feature on arc systems.
|
||
|
||
--*/
|
||
|
||
#include "ntddk.h"
|
||
#include "stdarg.h"
|
||
#include "stdio.h"
|
||
#include "ntdddisk.h"
|
||
#include "simbad.h"
|
||
|
||
#ifdef POOL_TAGGING
|
||
#ifdef ExAllocatePool
|
||
#undef ExAllocatePool
|
||
#endif
|
||
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'daBS')
|
||
#endif
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// SimBad debug level global variable
|
||
//
|
||
|
||
ULONG SimBadDebug = 1;
|
||
|
||
#define DebugPrint(X) SimBadDebugPrint X
|
||
|
||
VOID
|
||
SimBadDebugPrint(
|
||
ULONG DebugPrintLevel,
|
||
PCCHAR DebugMessage,
|
||
...
|
||
);
|
||
|
||
#else
|
||
|
||
#define DebugPrint(X)
|
||
|
||
#endif // DBG
|
||
|
||
//
|
||
// Pool debugging support - add unique tag to simbad allocations.
|
||
//
|
||
|
||
#ifdef POOL_TAGGING
|
||
#undef ExAllocatePool
|
||
#undef ExAllocatePoolWithQuota
|
||
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'BmiS')
|
||
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'BmiS')
|
||
#endif
|
||
|
||
//
|
||
// This macro has the effect of Bit = log2(Data)
|
||
//
|
||
|
||
#define WHICH_BIT(Data, Bit) { \
|
||
for (Bit = 0; Bit < 32; Bit++) { \
|
||
if ((Data >> Bit) == 1) { \
|
||
break; \
|
||
} \
|
||
} \
|
||
}
|
||
|
||
//
|
||
// Hal definitions that normal drivers would never call.
|
||
//
|
||
|
||
//
|
||
// Define the firmware routine types
|
||
//
|
||
|
||
typedef enum _FIRMWARE_REENTRY {
|
||
HalHaltRoutine,
|
||
HalPowerDownRoutine,
|
||
HalRestartRoutine,
|
||
HalRebootRoutine,
|
||
HalInteractiveModeRoutine,
|
||
HalMaximumRoutine
|
||
} FIRMWARE_REENTRY, *PFIRMWARE_REENTRY;
|
||
|
||
NTHALAPI
|
||
VOID
|
||
HalReturnToFirmware (
|
||
IN FIRMWARE_REENTRY Routine
|
||
);
|
||
|
||
NTHALAPI
|
||
BOOLEAN
|
||
HalMakeBeep(
|
||
IN ULONG Frequency
|
||
);
|
||
|
||
|
||
//
|
||
// Device Extension
|
||
//
|
||
|
||
typedef struct _DEVICE_EXTENSION {
|
||
|
||
//
|
||
// Back pointer to device object
|
||
//
|
||
|
||
PDEVICE_OBJECT DeviceObject;
|
||
|
||
//
|
||
// Target Device Object
|
||
//
|
||
|
||
PDEVICE_OBJECT TargetDeviceObject;
|
||
|
||
//
|
||
// Disk number for use in repartitioning.
|
||
//
|
||
|
||
ULONG DiskNumber;
|
||
|
||
//
|
||
// Driver object pointer for use in repartitioning.
|
||
//
|
||
|
||
PDRIVER_OBJECT DriverObject;
|
||
|
||
//
|
||
// Start byte of partition
|
||
//
|
||
|
||
LARGE_INTEGER PartitionOffset;
|
||
|
||
//
|
||
// Number of bytes in partition
|
||
//
|
||
|
||
LARGE_INTEGER PartitionLength;
|
||
|
||
//
|
||
// Partition number is used in repartitioning.
|
||
//
|
||
|
||
ULONG PartitionNumber;
|
||
|
||
//
|
||
// Chain for all objects created that represent partitions on
|
||
// a particular disk.
|
||
//
|
||
|
||
struct _DEVICE_EXTENSION *PartitionChain;
|
||
|
||
//
|
||
// Base pointer to partition zero on the disk
|
||
//
|
||
|
||
struct _DEVICE_EXTENSION *ZeroExtension;
|
||
|
||
//
|
||
// Sector size
|
||
//
|
||
|
||
ULONG SectorSize;
|
||
|
||
//
|
||
// Sector Shift Count
|
||
//
|
||
|
||
ULONG SectorShift;
|
||
|
||
//
|
||
// Signature for the device. This is used for storage in the registry.
|
||
//
|
||
|
||
ULONG DiskSignature;
|
||
|
||
//
|
||
// Simulated bad sector array
|
||
//
|
||
|
||
PSIMBAD_SECTORS SimBadSectors;
|
||
|
||
//
|
||
// Spinlock to protect queue accesses
|
||
//
|
||
|
||
KSPIN_LOCK SpinLock;
|
||
|
||
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
|
||
|
||
#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION)
|
||
|
||
|
||
//
|
||
// Function declarations
|
||
//
|
||
|
||
NTSTATUS
|
||
DriverEntry (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
VOID
|
||
SimBadInitialize(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN ULONG DeviceCount,
|
||
IN ULONG Count
|
||
);
|
||
|
||
NTSTATUS
|
||
SimBadCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SimBadReadWrite(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SimBadIoCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
SimBadDeviceControl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SimBadShutdownFlush(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SimbadNewDiskCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
|
||
#define PARTITION_INFO_SIZE (26 * sizeof(PARTITION_INFORMATION) + 4)
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
System entry point for SimBad driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - System representation of this driver.
|
||
RegistryPath - Not used.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Always returns success.
|
||
|
||
--*/
|
||
|
||
{
|
||
DebugPrint((1,
|
||
"Microsoft SIMBAD Device Driver\n"));
|
||
|
||
//
|
||
// Set up the device driver entry points.
|
||
//
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = SimBadCreate;
|
||
DriverObject->MajorFunction[IRP_MJ_READ] = SimBadReadWrite;
|
||
DriverObject->MajorFunction[IRP_MJ_WRITE] = SimBadReadWrite;
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SimBadDeviceControl;
|
||
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = SimBadShutdownFlush;
|
||
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = SimBadShutdownFlush;
|
||
|
||
SimBadInitialize(DriverObject,
|
||
0,
|
||
0);
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
} // end DriverEntry()
|
||
|
||
VOID
|
||
SimBadInitialize(
|
||
PDRIVER_OBJECT DriverObject,
|
||
ULONG DeviceCount,
|
||
ULONG Count
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attach to disk devices and initialize driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - System representation of this driver.
|
||
DeviceCount - Number of disk to which simbad attached so far.
|
||
Count - Not used.
|
||
|
||
Return Value:
|
||
|
||
Nothing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONFIGURATION_INFORMATION configurationInformation;
|
||
PDRIVE_LAYOUT_INFORMATION partitionInfo;
|
||
UCHAR deviceNameBuffer[128];
|
||
ANSI_STRING deviceName;
|
||
UNICODE_STRING unicodeDeviceName;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PDEVICE_OBJECT simbadDevice;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
PDEVICE_EXTENSION partitionZeroExtension;
|
||
PIRP irp;
|
||
PDISK_GEOMETRY diskGeometry;
|
||
KEVENT event;
|
||
NTSTATUS status;
|
||
ULONG disks;
|
||
ULONG partitions;
|
||
ULONG sectorShift;
|
||
|
||
//
|
||
// Get the configuration information for this driver.
|
||
//
|
||
|
||
configurationInformation = IoGetConfigurationInformation();
|
||
|
||
DebugPrint((2, "SimBadInitialize: Attaching to %d disks\n",
|
||
configurationInformation->DiskCount - DeviceCount));
|
||
|
||
//
|
||
// Allocate buffer for drive geometry.
|
||
//
|
||
|
||
diskGeometry = ExAllocatePool(NonPagedPool,
|
||
sizeof(DISK_GEOMETRY));
|
||
|
||
//
|
||
// Find disk devices.
|
||
//
|
||
|
||
for (disks = DeviceCount;
|
||
disks < configurationInformation->DiskCount;
|
||
disks++) {
|
||
|
||
//
|
||
// Create the device object for SimBad.
|
||
//
|
||
|
||
status = IoCreateDevice(DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
NULL,
|
||
FILE_DEVICE_DISK,
|
||
0,
|
||
FALSE,
|
||
&simbadDevice);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((1, "SimBadInitialize: failed create of %s\n",
|
||
deviceNameBuffer));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Point device extension back at device object.
|
||
//
|
||
|
||
deviceExtension = simbadDevice->DeviceExtension;
|
||
deviceExtension->DeviceObject = simbadDevice;
|
||
|
||
//
|
||
// Indicate device needs IRPs with MDLs.
|
||
//
|
||
|
||
simbadDevice->Flags |= DO_DIRECT_IO;
|
||
|
||
//
|
||
// Attach to partition0. This call links the newly created
|
||
// device to the target device, returning the target device object.
|
||
//
|
||
|
||
sprintf(deviceNameBuffer,
|
||
"\\Device\\Harddisk%d\\Partition0",
|
||
disks);
|
||
RtlInitAnsiString(&deviceName,
|
||
deviceNameBuffer);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
||
&deviceName,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(simbadDevice);
|
||
break;
|
||
}
|
||
|
||
status = IoAttachDevice(simbadDevice,
|
||
&unicodeDeviceName,
|
||
&deviceExtension->TargetDeviceObject);
|
||
RtlFreeUnicodeString(&unicodeDeviceName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(simbadDevice);
|
||
DebugPrint((1, "SimBadInitialize: failed attach to %s\n",
|
||
deviceNameBuffer));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Propogate driver's alignment requirements.
|
||
//
|
||
|
||
simbadDevice->AlignmentRequirement =
|
||
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
||
|
||
//
|
||
// Physical disk starts at byte offset 0.
|
||
//
|
||
|
||
deviceExtension->PartitionOffset.QuadPart = (LONGLONG)0;
|
||
|
||
//
|
||
// Set the event object to the unsignaled state.
|
||
// It will be used to signal request completion.
|
||
//
|
||
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
//
|
||
// Create IRP for get drive geometry device control.
|
||
//
|
||
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
||
deviceExtension->TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
diskGeometry,
|
||
sizeof(DISK_GEOMETRY),
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
|
||
if (!irp) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Call lower-level driver to process request.
|
||
//
|
||
|
||
status = IoCallDriver(deviceExtension->TargetDeviceObject,
|
||
irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Store number of bytes per sector.
|
||
//
|
||
|
||
deviceExtension->SectorSize = diskGeometry->BytesPerSector;
|
||
|
||
//
|
||
// Calculate and store sector shift.
|
||
//
|
||
|
||
WHICH_BIT(deviceExtension->SectorSize, sectorShift);
|
||
deviceExtension->SectorShift = sectorShift;
|
||
|
||
//
|
||
// This driver will not check if IO off end of physical device.
|
||
//
|
||
|
||
deviceExtension->PartitionLength.QuadPart = (LONGLONG) -1;
|
||
|
||
//
|
||
// Store information to be used during repartitioning.
|
||
//
|
||
|
||
deviceExtension->DiskNumber = disks;
|
||
deviceExtension->PartitionNumber = 0;
|
||
deviceExtension->DriverObject = DriverObject;
|
||
deviceExtension->PartitionChain = NULL;
|
||
deviceExtension->ZeroExtension = deviceExtension;
|
||
|
||
//
|
||
// Read the partition information for the device.
|
||
//
|
||
|
||
status = IoReadPartitionTable(deviceExtension->TargetDeviceObject,
|
||
diskGeometry->BytesPerSector,
|
||
TRUE,
|
||
&partitionInfo);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((1,
|
||
"SimBadInitialization: No drive layout for %s (%x)\n",
|
||
deviceNameBuffer,
|
||
status));
|
||
continue;
|
||
}
|
||
|
||
partitionZeroExtension = deviceExtension;
|
||
|
||
DebugPrint((2, "SimBadInitialize: Attaching to %d partitions\n",
|
||
partitionInfo->PartitionCount));
|
||
for (partitions = 0;
|
||
partitions < partitionInfo->PartitionCount;
|
||
partitions++) {
|
||
|
||
status = IoCreateDevice(DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
NULL,
|
||
FILE_DEVICE_DISK,
|
||
0,
|
||
FALSE,
|
||
&deviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Point device extension back at device object.
|
||
//
|
||
|
||
deviceExtension = deviceObject->DeviceExtension;
|
||
deviceExtension->DeviceObject = deviceObject;
|
||
|
||
//
|
||
// Indicate device needs IRPs with MDLs.
|
||
//
|
||
|
||
deviceObject->Flags |= DO_DIRECT_IO;
|
||
|
||
//
|
||
// Allocate and initialize memory to hold SimBad sectors.
|
||
// A pointer to this structure will be saved in every SimBad
|
||
// device extension for this disk.
|
||
//
|
||
|
||
deviceExtension->SimBadSectors = ExAllocatePool(NonPagedPool,
|
||
sizeof(SIMBAD_SECTORS));
|
||
|
||
if (deviceExtension->SimBadSectors) {
|
||
RtlZeroMemory(deviceExtension->SimBadSectors,
|
||
sizeof(SIMBAD_SECTORS));
|
||
}
|
||
|
||
deviceExtension->SectorSize = diskGeometry->BytesPerSector;
|
||
deviceExtension->SectorShift = sectorShift;
|
||
|
||
//
|
||
// Initialize spin lock for critical sections.
|
||
//
|
||
|
||
KeInitializeSpinLock(&deviceExtension->SpinLock);
|
||
|
||
//
|
||
// Store byte offset from beginning of disk.
|
||
//
|
||
|
||
deviceExtension->PartitionOffset =
|
||
partitionInfo->PartitionEntry[partitions].StartingOffset;
|
||
|
||
//
|
||
// Store number of bytes in partition.
|
||
//
|
||
|
||
deviceExtension->PartitionLength =
|
||
partitionInfo->PartitionEntry[partitions].PartitionLength;
|
||
|
||
//
|
||
// Store the disk signature for use when remembering bad sector
|
||
// lists in the registry.
|
||
//
|
||
|
||
deviceExtension->DiskSignature = partitionInfo->Signature;
|
||
|
||
//
|
||
// Attach to the partition. This call links the newly created
|
||
// device to the target device, returning the target device object.
|
||
//
|
||
|
||
sprintf(deviceNameBuffer,
|
||
"\\Device\\Harddisk%d\\Partition%d",
|
||
disks,
|
||
partitions + 1);
|
||
RtlInitAnsiString(&deviceName,
|
||
deviceNameBuffer);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
||
&deviceName,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
IoDeleteDevice(deviceObject);
|
||
continue;
|
||
}
|
||
status = IoAttachDevice(deviceObject,
|
||
&unicodeDeviceName,
|
||
&deviceExtension->TargetDeviceObject);
|
||
RtlFreeUnicodeString(&unicodeDeviceName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((1, "SimBadInitialize: failed attach %s\n",
|
||
deviceNameBuffer));
|
||
IoDeleteDevice(deviceObject);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Store information to be used during repartitioning.
|
||
// partitions is a zero biased number.
|
||
//
|
||
|
||
deviceExtension->DiskNumber = disks;
|
||
deviceExtension->PartitionNumber = partitions + 1;
|
||
deviceExtension->DriverObject = DriverObject;
|
||
deviceExtension->ZeroExtension = partitionZeroExtension;
|
||
deviceExtension->PartitionChain = partitionZeroExtension->PartitionChain;
|
||
partitionZeroExtension->PartitionChain = deviceExtension;
|
||
|
||
//
|
||
// Propogate driver's alignment requirements.
|
||
//
|
||
|
||
simbadDevice->AlignmentRequirement =
|
||
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
||
|
||
} // end for (partitions ...)
|
||
|
||
//
|
||
// Free space for the partition information.
|
||
//
|
||
|
||
ExFreePool(partitionInfo);
|
||
|
||
} // end for (disks ...)
|
||
|
||
//
|
||
// Free allocated data.
|
||
//
|
||
|
||
ExFreePool(diskGeometry);
|
||
|
||
//
|
||
// If this is the first time the initialization routine was called
|
||
// then register for a callback to attach to nonboot disks.
|
||
//
|
||
|
||
if (DeviceCount == 0) {
|
||
|
||
IoRegisterDriverReinitialization(DriverObject,
|
||
(PDRIVER_REINITIALIZE)SimBadInitialize,
|
||
(PVOID)configurationInformation->DiskCount);
|
||
}
|
||
|
||
return;
|
||
|
||
} // end SimBadInitialization()
|
||
|
||
|
||
NTSTATUS
|
||
SimBadCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine serves create commands. It does no more than
|
||
establish the drivers existance by returning status success.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject
|
||
IRP
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
|
||
} // end SimBadCreate()
|
||
|
||
|
||
NTSTATUS
|
||
SimBadReadWrite(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the driver entry point for read and write requests
|
||
to disks.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to device object for disk partition
|
||
Irp - NT IO Request Packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of request
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PSIMBAD_SECTORS badSectors = deviceExtension->SimBadSectors;
|
||
ULONG beginningSector;
|
||
ULONG endingSector;
|
||
ULONG sectorCount;
|
||
ULONG length;
|
||
ULONG i;
|
||
KIRQL currentIrql;
|
||
static ULONG Iter=0;
|
||
|
||
//
|
||
// Check that SimBad is enabled.
|
||
//
|
||
|
||
if (badSectors && badSectors->Enabled) {
|
||
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Check for orphan.
|
||
//
|
||
|
||
if (badSectors->Orphaned) {
|
||
Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_IO_DEVICE_ERROR;
|
||
}
|
||
|
||
if (badSectors->RandomWriteDrop) {
|
||
Iter++;
|
||
if ((Iter % badSectors->Seed) == 0) {
|
||
DbgPrint("Dropping a Write. Iter %d Seed %d\n", Iter, badSectors->Seed);
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
} else {
|
||
// DbgPrint("Not Dropping a Write Iter %d %d\n", Iter, badSectors->Seed);
|
||
}
|
||
}
|
||
|
||
if (badSectors->BugCheck){
|
||
PUCHAR lp = NULL;
|
||
UCHAR value;
|
||
|
||
DbgPrint("Simbad: System about to bug check...\n");
|
||
while(TRUE) {
|
||
value = *lp;
|
||
lp++;
|
||
|
||
//
|
||
// This DbgPrint uses value, so that the dereference is
|
||
// is not optimized out of the code, on free builds.
|
||
//
|
||
DbgPrint("Simbad: Prevent optimization emlimination... %d.\n", \
|
||
value);
|
||
}
|
||
}
|
||
|
||
if (badSectors->FirmwareReset){
|
||
LARGE_INTEGER liDelay = RtlConvertLongToLargeInteger(-100000);
|
||
|
||
DbgPrint("Simbad: System about to reset...\n");
|
||
HalMakeBeep( 1000 );
|
||
KeDelayExecutionThread( KernelMode, FALSE, &liDelay );
|
||
HalMakeBeep( 0 );
|
||
HalReturnToFirmware(HalRebootRoutine);
|
||
}
|
||
|
||
//
|
||
// Copy stack parameters to next stack.
|
||
//
|
||
|
||
RtlMoveMemory(nextIrpStack,
|
||
currentIrpStack,
|
||
sizeof(IO_STACK_LOCATION));
|
||
|
||
//
|
||
// Calculate number of sectors in this transfer.
|
||
//
|
||
|
||
sectorCount = currentIrpStack->Parameters.Read.Length >>
|
||
deviceExtension->SectorShift;
|
||
|
||
//
|
||
// Calculate beginning sector. This will only work if the result
|
||
// is contained entirely in the lowpart of the result.
|
||
//
|
||
|
||
beginningSector = (ULONG)
|
||
(currentIrpStack->Parameters.Read.ByteOffset.QuadPart >>
|
||
(CCHAR)deviceExtension->SectorShift);
|
||
|
||
//
|
||
// Calculate ending sector.
|
||
//
|
||
|
||
endingSector = beginningSector + sectorCount - 1;
|
||
|
||
//
|
||
// Acquire spinlock.
|
||
//
|
||
|
||
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
||
|
||
for (i = 0; i < badSectors->Count; i++) {
|
||
|
||
if ((badSectors->Sector[i].BlockAddress >= beginningSector) &&
|
||
(badSectors->Sector[i].BlockAddress <= endingSector)) {
|
||
|
||
if (((badSectors->Sector[i].AccessType & SIMBAD_ACCESS_READ) &&
|
||
(currentIrpStack->MajorFunction == IRP_MJ_READ)) ||
|
||
((badSectors->Sector[i].AccessType & SIMBAD_ACCESS_WRITE) &&
|
||
(currentIrpStack->MajorFunction == IRP_MJ_WRITE))) {
|
||
|
||
//
|
||
// Calculate the new length up to the bad sector.
|
||
//
|
||
|
||
length =
|
||
(badSectors->Sector[i].BlockAddress - beginningSector) <<
|
||
deviceExtension->SectorShift;
|
||
|
||
//
|
||
// Check if this the first bad sector in the request.
|
||
//
|
||
|
||
if (length == 0) {
|
||
|
||
KeReleaseSpinLock(&deviceExtension->SpinLock,
|
||
currentIrql);
|
||
|
||
//
|
||
// Complete this request.
|
||
//
|
||
|
||
Irp->IoStatus.Status = badSectors->Sector[i].Status;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return badSectors->Sector[i].Status;
|
||
|
||
} else if (length < nextIrpStack->Parameters.Read.Length) {
|
||
|
||
//
|
||
// Reduce bytes requested to number before bad sector.
|
||
//
|
||
|
||
nextIrpStack->Parameters.Read.Length = length;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
||
|
||
//
|
||
// Set completion routine callback.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
SimBadIoCompletion,
|
||
deviceExtension,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Simbad is disabled. Set stack back to hide simbad.
|
||
//
|
||
|
||
Irp->CurrentLocation++;
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
}
|
||
|
||
//
|
||
// Call target driver.
|
||
//
|
||
|
||
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
||
|
||
} // end SimBadReadWrite()
|
||
|
||
|
||
NTSTATUS
|
||
SimBadIoCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when the I/O request has completed only if
|
||
SimBad is enabled for this partition. The routine checks the I/O request
|
||
to see if a sector involved in the request is to be failed.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - SimBad device object.
|
||
Irp - Completed request.
|
||
Context - not used. Set up to also be a pointer to the DeviceObject.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) Context;
|
||
PSIMBAD_SECTORS badSectors = deviceExtension->SimBadSectors;
|
||
ULONG beginningSector;
|
||
ULONG endingSector;
|
||
ULONG sectorCount;
|
||
ULONG i;
|
||
KIRQL currentIrql;
|
||
|
||
//
|
||
// Check if some other error occurred.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
||
return(Irp->IoStatus.Status);
|
||
}
|
||
|
||
//
|
||
// Get current stack.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Check for VERIFY SECTOR IOCTL (Format).
|
||
//
|
||
|
||
if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
||
|
||
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Get starting offset and length from verify parameters.
|
||
// Convert from byte to sector counts.
|
||
//
|
||
|
||
beginningSector = (ULONG)(verifyInfo->StartingOffset.QuadPart >>
|
||
(CCHAR)deviceExtension->SectorShift);
|
||
sectorCount = verifyInfo->Length >> deviceExtension->SectorShift;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Calculate number of sectors in this transfer.
|
||
//
|
||
|
||
sectorCount = irpStack->Parameters.Read.Length >>
|
||
deviceExtension->SectorShift;
|
||
|
||
//
|
||
// Calculate beginning sector. This will only work if the result
|
||
// is contained entirely in the lowpart of the result.
|
||
//
|
||
|
||
beginningSector = (ULONG)(irpStack->Parameters.Read.ByteOffset.QuadPart >>
|
||
(CCHAR)deviceExtension->SectorShift);
|
||
}
|
||
|
||
//
|
||
// Calculate ending sector.
|
||
//
|
||
|
||
endingSector = beginningSector + sectorCount - 1;
|
||
DebugPrint((4, "SimBadIoCompletion: I/O for 0x%x to 0x%x\n",
|
||
beginningSector,
|
||
endingSector));
|
||
|
||
//
|
||
// Acquire spinlock.
|
||
//
|
||
|
||
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
||
|
||
for (i = 0; i < badSectors->Count; i++) {
|
||
|
||
if ((badSectors->Sector[i].BlockAddress >= beginningSector) &&
|
||
(badSectors->Sector[i].BlockAddress <= endingSector)) {
|
||
|
||
//
|
||
// Request includes this simulated bad sector.
|
||
//
|
||
|
||
DebugPrint((1, "SimBadIoCompletion: Bad sector %x\n",
|
||
badSectors->Sector[i].BlockAddress,
|
||
DeviceObject));
|
||
|
||
if (((badSectors->Sector[i].AccessType & SIMBAD_ACCESS_READ) &&
|
||
(irpStack->MajorFunction == IRP_MJ_READ)) ||
|
||
((badSectors->Sector[i].AccessType & SIMBAD_ACCESS_VERIFY) &&
|
||
(irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)) ||
|
||
((badSectors->Sector[i].AccessType & SIMBAD_ACCESS_WRITE) &&
|
||
(irpStack->MajorFunction == IRP_MJ_WRITE))) {
|
||
|
||
//
|
||
// Update the information field to reflect the location
|
||
// of the failure.
|
||
//
|
||
|
||
if (badSectors->Sector[i].AccessType & SIMBAD_ACCESS_ERROR_ZERO_OFFSET) {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
Irp->IoStatus.Status = badSectors->Sector[i].Status;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
return STATUS_SUCCESS;
|
||
|
||
} // SimBadIoCompletion()
|
||
|
||
|
||
NTSTATUS
|
||
SimBadUpdateDriveLayout(
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called after an IOCTL to set drive layout completes.
|
||
It attempts to attach to each partition in the system. If it fails
|
||
then it is assumed that simbad has already attached.
|
||
|
||
After all partitions are attached a pass is made on all disk
|
||
partitions to obtain the partition information and determine if
|
||
existing partitions were modified by the IOCTL.
|
||
|
||
Arguments:
|
||
|
||
PhysicalDeviceObject - Pointer to device object for the disk just changed.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION physicalExtension = PhysicalDeviceObject->DeviceExtension;
|
||
ULONG partitionNumber = 0;
|
||
PPARTITION_INFORMATION partitionInformation;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PDISK_GEOMETRY diskGeometry;
|
||
PDEVICE_OBJECT targetObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
PDEVICE_EXTENSION deviceExtension;
|
||
UCHAR ntDeviceName[64];
|
||
STRING ntString;
|
||
UNICODE_STRING ntUnicodeString;
|
||
PFILE_OBJECT fileObject;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Attach to any new partitions created by the set layout call.
|
||
// Determine what hasn't been attached by walking through the list of
|
||
// existing objects and taking the highest value for the partition
|
||
// number.
|
||
//
|
||
|
||
deviceExtension = physicalExtension->PartitionChain;
|
||
|
||
while (deviceExtension) {
|
||
if (deviceExtension->PartitionNumber > partitionNumber) {
|
||
partitionNumber = deviceExtension->PartitionNumber;
|
||
}
|
||
deviceExtension = deviceExtension->PartitionChain;
|
||
}
|
||
|
||
do {
|
||
|
||
//
|
||
// Get next partition. Already attached to the partition number located.
|
||
//
|
||
|
||
partitionNumber++;
|
||
|
||
//
|
||
// Create unicode NT device name.
|
||
//
|
||
|
||
sprintf(ntDeviceName,
|
||
"\\Device\\Harddisk%d\\Partition%d",
|
||
physicalExtension->DiskNumber,
|
||
partitionNumber);
|
||
|
||
RtlInitAnsiString(&ntString,
|
||
ntDeviceName);
|
||
|
||
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
||
&ntString,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get target device object.
|
||
//
|
||
|
||
status = IoGetDeviceObjectPointer(&ntUnicodeString,
|
||
FILE_READ_ATTRIBUTES,
|
||
&fileObject,
|
||
&targetObject);
|
||
|
||
//
|
||
// If this fails then it is because there is no such device
|
||
// which signals completion.
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Dereference file object as these are the rules.
|
||
//
|
||
|
||
ObDereferenceObject(fileObject);
|
||
|
||
//
|
||
// Check if this device is already mounted.
|
||
//
|
||
|
||
if ((!targetObject->Vpb) || (targetObject->Vpb->Flags & VPB_MOUNTED)) {
|
||
|
||
//
|
||
// Assume this device has already been attached.
|
||
//
|
||
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Create device object for this partition.
|
||
//
|
||
|
||
status = IoCreateDevice(physicalExtension->DriverObject,
|
||
sizeof(DEVICE_EXTENSION),
|
||
NULL,
|
||
FILE_DEVICE_DISK,
|
||
0,
|
||
FALSE,
|
||
&deviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Point device extension back at device object.
|
||
//
|
||
|
||
deviceExtension = deviceObject->DeviceExtension;
|
||
deviceExtension->DeviceObject = deviceObject;
|
||
deviceObject->Flags |= DO_DIRECT_IO;
|
||
|
||
//
|
||
// Attach to the partition. This call links the newly created
|
||
// device to the target device, returning the target device object.
|
||
//
|
||
|
||
status = IoAttachDevice(deviceObject,
|
||
&ntUnicodeString,
|
||
&deviceExtension->TargetDeviceObject);
|
||
RtlFreeUnicodeString(&ntUnicodeString);
|
||
|
||
if ((!NT_SUCCESS(status)) || (status == STATUS_OBJECT_NAME_EXISTS)) {
|
||
|
||
//
|
||
// Assume this device is already attached.
|
||
//
|
||
|
||
IoDeleteDevice(deviceObject);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Store disk/driver information.
|
||
//
|
||
|
||
deviceExtension->DiskNumber = physicalExtension->DiskNumber;
|
||
deviceExtension->PartitionNumber = partitionNumber;
|
||
deviceExtension->DriverObject = physicalExtension->DriverObject;
|
||
|
||
//
|
||
// Allocate and initialize memory to hold SimBad sectors.
|
||
// A pointer to this structure will be saved in every SimBad
|
||
// device extension for this disk.
|
||
//
|
||
|
||
deviceExtension->SimBadSectors = ExAllocatePool(NonPagedPool,
|
||
sizeof(SIMBAD_SECTORS));
|
||
|
||
if (deviceExtension->SimBadSectors) {
|
||
RtlZeroMemory(deviceExtension->SimBadSectors,
|
||
sizeof(SIMBAD_SECTORS));
|
||
}
|
||
|
||
//
|
||
// Initialize spin lock for critical sections.
|
||
//
|
||
|
||
KeInitializeSpinLock(&deviceExtension->SpinLock);
|
||
|
||
//
|
||
// Store the disk signature for use when remembering bad sector
|
||
// lists in the registry.
|
||
//
|
||
|
||
deviceExtension->DiskSignature = physicalExtension->DiskSignature;
|
||
|
||
//
|
||
// Propogate driver's alignment requirements.
|
||
//
|
||
|
||
deviceObject->AlignmentRequirement =
|
||
deviceExtension->TargetDeviceObject->AlignmentRequirement;
|
||
|
||
//
|
||
// Assume some sizes in case the partition update loop
|
||
// performed below fails.
|
||
//
|
||
|
||
deviceExtension->SectorSize = 512;
|
||
deviceExtension->SectorShift = 9;
|
||
deviceExtension->PartitionOffset.QuadPart = (LONGLONG)0;
|
||
deviceExtension->PartitionLength.QuadPart = (LONGLONG)0;
|
||
|
||
//
|
||
// Chain this new device into the partition chain.
|
||
//
|
||
|
||
deviceExtension->PartitionChain = physicalExtension->PartitionChain;
|
||
physicalExtension->PartitionChain = deviceExtension;
|
||
|
||
} while (TRUE);
|
||
|
||
//
|
||
// Allocate memory for this work.
|
||
//
|
||
|
||
diskGeometry = (PDISK_GEOMETRY) ExAllocatePool(NonPagedPool,
|
||
sizeof(DISK_GEOMETRY));
|
||
partitionInformation = (PPARTITION_INFORMATION) ExAllocatePool(NonPagedPool,
|
||
sizeof(PARTITION_INFORMATION));
|
||
|
||
if (!diskGeometry || !partitionInformation) {
|
||
|
||
//
|
||
// Could not allocate the two blocks of memory.
|
||
// Any new partitions will have a size of zero and any partitions
|
||
// the were deleted will not be set to the forced off state.
|
||
//
|
||
|
||
if (diskGeometry) {
|
||
ExFreePool(diskGeometry);
|
||
}
|
||
if (partitionInformation) {
|
||
ExFreePool(partitionInformation);
|
||
}
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Update all of the partitions on this disk to reflect the new
|
||
// drive layout.
|
||
//
|
||
|
||
for (deviceExtension = physicalExtension->PartitionChain;
|
||
deviceExtension;
|
||
deviceExtension = deviceExtension->PartitionChain) {
|
||
|
||
KEVENT event;
|
||
ULONG sectorShift;
|
||
PIRP irp;
|
||
|
||
//
|
||
// Pick up the geometry.
|
||
//
|
||
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
||
deviceExtension->TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
diskGeometry,
|
||
sizeof(DISK_GEOMETRY),
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
if (irp) {
|
||
|
||
//
|
||
// Call lower-level driver for geometry.
|
||
//
|
||
|
||
status = IoCallDriver(deviceExtension->TargetDeviceObject,
|
||
irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Pick up the partition information for this partition.
|
||
//
|
||
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
|
||
deviceExtension->TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
partitionInformation,
|
||
sizeof(PARTITION_INFORMATION),
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
if (irp) {
|
||
|
||
//
|
||
// Call lower-level driver for partition information.
|
||
//
|
||
|
||
status = IoCallDriver(deviceExtension->TargetDeviceObject,
|
||
irp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Both the partition information and the geometry
|
||
// where successfully obtained.
|
||
//
|
||
|
||
deviceExtension->SectorSize = diskGeometry->BytesPerSector;
|
||
WHICH_BIT(deviceExtension->SectorSize, sectorShift);
|
||
deviceExtension->SectorShift = sectorShift;
|
||
|
||
//
|
||
// Store the partition offset and size.
|
||
//
|
||
|
||
deviceExtension->PartitionOffset = partitionInformation->StartingOffset;
|
||
deviceExtension->PartitionLength = partitionInformation->PartitionLength;
|
||
}
|
||
|
||
//
|
||
// If the partition has no size, make sure the bad sector
|
||
// list is turned off.
|
||
//
|
||
|
||
if (!deviceExtension->PartitionLength.QuadPart) {
|
||
deviceExtension->SimBadSectors->Enabled = FALSE;
|
||
}
|
||
}
|
||
ExFreePool(diskGeometry);
|
||
ExFreePool(partitionInformation);
|
||
return Irp->IoStatus.Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SimBadDeviceControl(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O subsystem for device controls.
|
||
It traps the SimBad specific device controls and forwards the others
|
||
to the lower drivers.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject
|
||
Irp
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PSIMBAD_DATA simBadDataIn = Irp->AssociatedIrp.SystemBuffer;
|
||
PSIMBAD_DATA simBadDataOut = Irp->UserBuffer;
|
||
PSIMBAD_SECTORS simBadSectors = deviceExtension->SimBadSectors;
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
ULONG j;
|
||
|
||
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
case IOCTL_DISK_SIMBAD:
|
||
|
||
switch (simBadDataIn->Function) {
|
||
|
||
case SIMBAD_ADD_SECTORS:
|
||
|
||
for (i=0, j=simBadSectors->Count; i<simBadDataIn->Count; i++, j++) {
|
||
|
||
if (j < MAXIMUM_SIMBAD_SECTORS) {
|
||
|
||
//
|
||
// Add sector to array.
|
||
//
|
||
|
||
simBadSectors->Sector[j] = simBadDataIn->Sector[i];
|
||
simBadSectors->Count++;
|
||
|
||
} else {
|
||
|
||
break;
|
||
}
|
||
} // end for (i ...)
|
||
|
||
//
|
||
// Update count with number of sectors added.
|
||
//
|
||
|
||
simBadDataIn->Count = j;
|
||
|
||
//
|
||
// If any sectors added return success.
|
||
//
|
||
|
||
if (simBadDataIn->Count) {
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
break;
|
||
|
||
case SIMBAD_REMOVE_SECTORS:
|
||
|
||
for (i = 0; i < simBadDataIn->Count; i++) {
|
||
|
||
for (j = 0; j < simBadSectors->Count; j++) {
|
||
|
||
if (simBadSectors->Sector[j].BlockAddress ==
|
||
simBadDataIn->Sector[i].BlockAddress) {
|
||
|
||
ULONG k;
|
||
|
||
//
|
||
// Remove sectors from driver's array.
|
||
//
|
||
|
||
for (k = j + 1; k < simBadSectors->Count; k++) {
|
||
|
||
//
|
||
// Shuffle array down to fill hole.
|
||
//
|
||
|
||
simBadSectors->Sector[k-1]=simBadSectors->Sector[k];
|
||
} // end for (k= ...)
|
||
|
||
//
|
||
// Update driver's bad sector count.
|
||
//
|
||
|
||
simBadSectors->Count--;
|
||
|
||
//
|
||
// Break out of middle loop.
|
||
//
|
||
|
||
break;
|
||
|
||
} // end if (simBadSectors ...)
|
||
} // end for (j= ...)
|
||
} // end for (i== ...)
|
||
|
||
//
|
||
// Update count with number of sectors removed.
|
||
//
|
||
|
||
simBadDataIn->Count -= i;
|
||
|
||
//
|
||
// If all sectors removed return success.
|
||
//
|
||
|
||
if (simBadDataIn->Count) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
break;
|
||
|
||
case SIMBAD_LIST_BAD_SECTORS:
|
||
|
||
if (simBadSectors == NULL) {
|
||
simBadDataOut->Count = 0;
|
||
} else {
|
||
|
||
DebugPrint((4, "SimBadDeviceControl: Returning %d entries\n",
|
||
simBadSectors->Count));
|
||
for (i = 0; i < simBadSectors->Count; i++) {
|
||
|
||
//
|
||
// Write sector to array.
|
||
//
|
||
|
||
DebugPrint((4,
|
||
"SimBadDeviceControl: Block %d Status %x Access %x\n",
|
||
simBadSectors->Sector[i].BlockAddress,
|
||
simBadSectors->Sector[i].Status,
|
||
simBadSectors->Sector[i].AccessType));
|
||
|
||
simBadDataOut->Sector[i] = simBadSectors->Sector[i];
|
||
}
|
||
|
||
simBadDataOut->Count = simBadSectors->Count;
|
||
}
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_ENABLE:
|
||
|
||
//
|
||
// Enable SIMBAD checking in driver.
|
||
//
|
||
|
||
simBadSectors->Enabled = TRUE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_DISABLE:
|
||
|
||
//
|
||
// Disable SIMBAD checking in driver.
|
||
//
|
||
|
||
simBadSectors->Enabled = FALSE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_CLEAR:
|
||
|
||
//
|
||
// Clear bad sector list.
|
||
// Also remove the orphaned state.
|
||
//
|
||
|
||
simBadSectors->Count = 0;
|
||
simBadSectors->Orphaned = FALSE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_ORPHAN:
|
||
|
||
//
|
||
// Orphan device. All accesses the this disk will fail.
|
||
//
|
||
|
||
DebugPrint((1,
|
||
"SimBadDeviceControl: Orphan this device\n"));
|
||
simBadSectors->Orphaned = TRUE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_RANDOM_WRITE_FAIL:
|
||
|
||
//
|
||
// Fails write randomly
|
||
//
|
||
|
||
DbgPrint(
|
||
"SimBadDeviceControl: Failing writes randomly\n");
|
||
simBadSectors->RandomWriteDrop = TRUE;
|
||
simBadSectors->Seed = simBadDataIn->Count;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_BUG_CHECK:
|
||
|
||
//
|
||
// Bug check the system
|
||
//
|
||
|
||
simBadSectors->BugCheck=TRUE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SIMBAD_FIRMWARE_RESET:
|
||
|
||
//
|
||
// Reset the system.
|
||
//
|
||
|
||
simBadSectors->FirmwareReset=TRUE;
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
|
||
DebugPrint((1,
|
||
"SimBadDeviceControl: Unsupported SIMBAD function\n"));
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
break;
|
||
|
||
} // end switch (simBadDataIn->Function ...)
|
||
|
||
break;
|
||
|
||
case IOCTL_DISK_REASSIGN_BLOCKS:
|
||
{
|
||
PREASSIGN_BLOCKS blockList = Irp->AssociatedIrp.SystemBuffer;
|
||
BOOLEAN missedOne = FALSE;
|
||
BOOLEAN sectorFound = FALSE;
|
||
ULONG startingSector = (ULONG)
|
||
(deviceExtension->PartitionOffset.QuadPart >>
|
||
(CCHAR)deviceExtension->SectorShift);
|
||
KIRQL currentIrql;
|
||
|
||
//
|
||
// The layer above is attempting a sector map. Check to see
|
||
// if the sectors being fixed are owned by SimBad and if SimBad
|
||
// will allow them to be fixed.
|
||
//
|
||
|
||
if (simBadSectors == NULL) {
|
||
goto CopyDown;
|
||
}
|
||
|
||
//
|
||
// Acquire spinlock.
|
||
//
|
||
|
||
KeAcquireSpinLock(&deviceExtension->SpinLock, ¤tIrql);
|
||
|
||
for (i = 0; i < (ULONG)blockList->Count; i++) {
|
||
for (j = 0; j < simBadSectors->Count; j++) {
|
||
|
||
if (blockList->BlockNumber[i] ==
|
||
(simBadSectors->Sector[j].BlockAddress + startingSector)) {
|
||
|
||
ULONG k;
|
||
|
||
//
|
||
// It is a SimBad sector. Check if do not remove
|
||
// flag is set.
|
||
//
|
||
|
||
if (simBadSectors->Sector[i].AccessType &
|
||
SIMBAD_ACCESS_FAIL_REASSIGN_SECTOR) {
|
||
break;
|
||
}
|
||
|
||
sectorFound = TRUE;
|
||
|
||
//
|
||
// Remove sectors from driver's array.
|
||
//
|
||
|
||
for (k = j + 1; k < simBadSectors->Count; k++) {
|
||
|
||
//
|
||
// Shuffle array down to fill hole.
|
||
//
|
||
|
||
simBadSectors->Sector[k-1]=simBadSectors->Sector[k];
|
||
}
|
||
|
||
//
|
||
// Update driver's bad sector count.
|
||
//
|
||
|
||
simBadSectors->Count--;
|
||
|
||
//
|
||
// If the accesstype bit is set to indicate that the
|
||
// physical device drivers should actually map out the
|
||
// bad sectors, then drop down to the copy stack code.
|
||
// Note that an assumption is made that there are no
|
||
// more bad sectors in the list and the bad sector is
|
||
// gone from SimBad's list (regardless of whether the
|
||
// lower drivers successfully map out the bad sector).
|
||
//
|
||
|
||
if (simBadSectors->Sector[j].AccessType &
|
||
SIMBAD_ACCESS_CAN_REASSIGN_SECTOR) {
|
||
|
||
DebugPrint((1,
|
||
"SimbadDeviceControl: Let physical disk map this sector\n"));
|
||
|
||
missedOne = TRUE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
} // next j
|
||
|
||
if (sectorFound) {
|
||
|
||
DebugPrint((1,
|
||
"SimBadDeviceControl: Removing bad block %x\n",
|
||
blockList->BlockNumber[i] - startingSector));
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
|
||
DebugPrint((1,
|
||
"SimBadDeviceControl: Block %x not found\n",
|
||
blockList->BlockNumber[i] - startingSector));
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
} // next i
|
||
|
||
KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);
|
||
|
||
//
|
||
// Assume only one sector gets mapped per request.
|
||
// To date this is a safe assumption.
|
||
//
|
||
|
||
if (missedOne) {
|
||
|
||
//
|
||
// Go to copy down stack.
|
||
//
|
||
|
||
goto CopyDown;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case IOCTL_DISK_VERIFY:
|
||
|
||
//
|
||
// Call ReadWrite routine. It does the right things.
|
||
//
|
||
|
||
return SimBadReadWrite(DeviceObject,
|
||
Irp);
|
||
|
||
case IOCTL_DISK_SET_DRIVE_LAYOUT: {
|
||
PIRP newIrp;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
KEVENT event;
|
||
CCHAR boost;
|
||
PDRIVE_LAYOUT_INFORMATION driveLayout =
|
||
(PDRIVE_LAYOUT_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Perform the set drive layout synchronously. Set both
|
||
// the input and output buffers as the buffer passed.
|
||
//
|
||
|
||
KeInitializeEvent(&event,
|
||
NotificationEvent,
|
||
FALSE);
|
||
newIrp = IoBuildDeviceIoControlRequest(IOCTL_DISK_SET_DRIVE_LAYOUT,
|
||
deviceExtension->TargetDeviceObject,
|
||
driveLayout,
|
||
currentIrpStack->Parameters.DeviceIoControl.InputBufferLength,
|
||
driveLayout,
|
||
currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
|
||
FALSE,
|
||
&event,
|
||
&ioStatusBlock);
|
||
|
||
status = IoCallDriver(deviceExtension->TargetDeviceObject, newIrp);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&event,
|
||
Suspended,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
|
||
Irp->IoStatus = ioStatusBlock;
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Process the new partition table. The work for the
|
||
// set drive layout was done synchronously because this
|
||
// routine performs synchronous activities.
|
||
//
|
||
|
||
SimBadUpdateDriveLayout(DeviceObject, Irp);
|
||
boost = IO_DISK_INCREMENT;
|
||
} else {
|
||
boost = IO_NO_INCREMENT;
|
||
}
|
||
IoCompleteRequest(Irp, boost);
|
||
return status;
|
||
}
|
||
|
||
case IOCTL_DISK_FIND_NEW_DEVICES:
|
||
|
||
//
|
||
// Copy current stack to next stack.
|
||
//
|
||
|
||
*nextIrpStack = *currentIrpStack;
|
||
|
||
//
|
||
// Ask to be called back during request completion.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
SimbadNewDiskCompletion,
|
||
(PVOID)IoGetConfigurationInformation()->DiskCount,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
//
|
||
// Call target driver.
|
||
//
|
||
|
||
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
||
|
||
default:
|
||
|
||
DebugPrint((5,"SimBadDeviceControl: Unsupported device IOCTL\n"));
|
||
|
||
CopyDown:
|
||
|
||
//
|
||
// Copy stack parameters to next stack.
|
||
//
|
||
|
||
*nextIrpStack = *currentIrpStack;
|
||
|
||
//
|
||
// Set IRP so IoComplete does not call completion routine
|
||
// for this driver.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
NULL,
|
||
deviceExtension,
|
||
FALSE,
|
||
FALSE,
|
||
FALSE);
|
||
|
||
//
|
||
// Pass unrecognized device control requests
|
||
// down to next driver layer.
|
||
//
|
||
|
||
return IoCallDriver(deviceExtension->TargetDeviceObject,
|
||
Irp);
|
||
} // end switch
|
||
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return status;
|
||
} // end SimBadDeviceControl()
|
||
|
||
|
||
NTSTATUS
|
||
SimBadShutdownFlush(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for a shutdown and flush IRPs. These are sent by the
|
||
system before it actually shuts down or when the file system does a flush.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to device object to being shutdown by system.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Set current stack back one.
|
||
//
|
||
|
||
Irp->CurrentLocation++,
|
||
Irp->Tail.Overlay.CurrentStackLocation++;
|
||
|
||
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
||
|
||
} // end SimBadShutdownFlush()
|
||
|
||
|
||
NTSTATUS
|
||
SimbadNewDiskCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the completion routine for IOCTL_DISK_FIND_NEW_DEVICES.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to device object to being shutdown by system.
|
||
Irp - IRP involved.
|
||
Context - Previous disk count.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_EXTENSION deviceExtension =
|
||
(PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Find new disk devices and attach to disk and all of its partitions.
|
||
//
|
||
|
||
SimBadInitialize(DeviceObject->DriverObject, (ULONG)Context, 0);
|
||
|
||
return Irp->IoStatus.Status;
|
||
}
|
||
|
||
VOID
|
||
SimBadRememberBadSectors(
|
||
PSIMBAD_SECTORS BadSectors
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will save the bad sector array in the registry. By doing
|
||
this then all subsequent reboots of the system will have the same bad
|
||
sector list.
|
||
|
||
Arguments:
|
||
|
||
BadSectors - pointer to the bad sector array to save in registry.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
}
|
||
|
||
#if DBG
|
||
|
||
|
||
VOID
|
||
SimBadDebugPrint(
|
||
ULONG DebugPrintLevel,
|
||
PCCHAR DebugMessage,
|
||
...
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Debug print for SimBad driver
|
||
|
||
Arguments:
|
||
|
||
Debug print level between 0 and 3, with 3 being the most verbose.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
va_list ap;
|
||
|
||
va_start( ap, DebugMessage );
|
||
|
||
if (DebugPrintLevel <= SimBadDebug) {
|
||
|
||
char buffer[128];
|
||
|
||
vsprintf(buffer, DebugMessage, ap);
|
||
DbgPrint(buffer);
|
||
}
|
||
|
||
va_end(ap);
|
||
|
||
} // end SimBadDebugPrint
|
||
|
||
#endif // DBG
|