NT4/private/ntos/dd/abios/abiosdsk.c
2020-09-30 17:12:29 +02:00

3663 lines
98 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
abiosdsk.c
Abstract:
This driver services disk devices through the standard
ABIOS disk compatibility interface.
Author:
Current Author
Bob Rinne (bobri) 10-Jul-1992
Initial Author
Mike Glass (mglass) 1-Aug-1991
Environment:
kernel mode only
Notes:
Revision History:
10-Jul-1992 bobri - Added page buffer to account for changes in memory
management.
2-Sep-1992 bobri - Added verify support.
1-Mar-1993 jhavens - Added AdapterObject support to remove internal
buffering and get larger I/Os
13-Nov-1993 mglass - Added routine UpdateDeviceObjects to support dynamic
disk repartitioning.
--*/
#include "ntddk.h"
#include "stdarg.h"
#include "stdio.h"
#if defined(i386)
#include "ntdddisk.h"
#include "abiosdev.h"
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'sobA')
#endif
//
// Abios support routine definitions.
//
NTSTATUS
KeI386AbiosCall(
IN USHORT LogicalId,
IN PDRIVER_OBJECT DriverObject,
IN PUCHAR RequestBlock,
IN USHORT EntryPoint
);
NTSTATUS
KeI386FlatToGdtSelector(
IN ULONG SelectorBase,
IN USHORT Length,
IN USHORT Selector
);
NTSTATUS
KeI386ReleaseLid(
IN USHORT LogicalId,
IN PDRIVER_OBJECT DriverObject
);
NTSTATUS
KeI386GetLid(
IN USHORT DeviceId,
IN USHORT RelativeLid,
IN BOOLEAN SharedLid,
IN PDRIVER_OBJECT DriverObject,
OUT PUSHORT LogicalId
);
NTSTATUS
KeI386AllocateGdtSelectors(
OUT PUSHORT SelectorArray,
IN USHORT NumberOfSelectors
);
NTSTATUS
KeI386ReleaseGdtSelectors(
OUT PUSHORT SelectorArray,
IN USHORT NumberOfSelectors
);
#if DBG
//
// ABIOS debug level global variable
//
ULONG AbiosDebug = 0;
#endif // DBG
//
// Controller extension
//
typedef struct _CONTROLLER_EXTENSION {
//
// Back pointer to controller object
//
PCONTROLLER_OBJECT ControllerObject;
//
// Pointer to Adapter object for DMA and synchronization.
//
PADAPTER_OBJECT DmaAdapterObject;
//
// The map register base for the adapter object.
//
PVOID MapRegisterBase;
//
// The maximum number of pages that the transfer can span.
//
ULONG MaximumNumberOfPages;
//
// Request timeout value
//
LARGE_INTEGER TimeoutValue;
//
// Current request
//
PIRP IrpAddress;
//
// Number of map registers allocated.
//
ULONG NumberOfMapRegisters;
//
// Pointer to driver object used in ABIOS calls as identifier of
// ownership of LID
//
PDRIVER_OBJECT DriverObject;
PKINTERRUPT InterruptObject;
ULONG Irq;
//
// DMA arbitration level from ABIOS
//
UCHAR ArbitrationLevel;
//
// Attempting reset of device.
//
BOOLEAN PerformingReset;
//
// Data Selector: selector to frame data.
//
USHORT DataSelector;
//
// Number of retries left.
//
USHORT RetryCount;
//
// Number of disks on this controller
//
USHORT UnitCount;
//
// ABIOS indentifier
//
USHORT LogicalId;
//
// ABIOS request block size and pointer.
//
USHORT RequestBlockLength;
PABIOS_REQUEST_BLOCK RequestBlock;
PABIOS_REQUEST_BLOCK RetryRequestBlock;
//
// 16:16 Selector points to ABIOS request block.
//
PUCHAR Selector;
//
// Current device object
//
PDEVICE_OBJECT DeviceObject;
//
// Total sectors for this IRP
//
ULONG TotalSectors;
//
// Remaining sectors in this request
//
ULONG RemainingSectors;
//
// Sectors this transfer
//
ULONG TransferSectors;
//
// Starting sector on disk
//
ULONG StartingSector;
//
// Virtual address of data buffer.
//
PVOID VirtualAddress;
//
// System address for PIO devices.
//
PVOID SystemAddress;
//
// Physical Address of data buffer.
//
ULONG PhysicalAddress;
//
// Number of bytes transferred
//
ULONG BytesTransferred;
KDPC CompletionDpc;
KTIMER Timer;
KDPC TimerDpc;
} CONTROLLER_EXTENSION, *PCONTROLLER_EXTENSION;
#define CONTROLLER_EXTENSION_SIZE sizeof(CONTROLLER_EXTENSION)
//
// Device Extension
//
typedef struct _DEVICE_EXTENSION {
//
// Back pointer to device object
//
PDEVICE_OBJECT DeviceObject;
//
// Pointer to controller extension
//
PCONTROLLER_EXTENSION ControllerExtension;
//
// Pointer to physical disk extension.
//
struct _DEVICE_EXTENSION *PhysicalDevice;
//
// Unit number on controller
//
USHORT Unit;
//
// Ordinal of partition represented by this device object
//
USHORT PartitionNumber;
//
// Partition type of this device object
//
// This field is set by:
//
// 1) Initially set according to the partition list entry partition
// type returned by IoReadPartitionTable.
//
// 2) Subsequently set by the IOCTL_DISK_SET_PARTITION_INFORMATION
// I/O control function when IoSetPartitionInformation function
// successfully updates the partition type on the disk.
//
UCHAR PartitionType;
//
// Boot indicator - indicates whether this partition is a bootable (active)
// partition for this device
//
// This field is set according to the partition list entry boot indicator
// returned by IoReadPartitionTable.
//
BOOLEAN BootIndicator;
//
// Log2 of sector size
//
UCHAR SectorShift;
//
// PAGE_SIZE / Sector size
//
UCHAR SectorsInPage;
//
// Maximum number of sectors that can be transfered
// in single request block
//
USHORT MaximumNumberBlocks;
//
// ABIOS suggested number of software retries
//
USHORT SoftwareRetries;
//
// System disk number
//
ULONG DiskNumber;
//
// Link to next partition for dynamic partitioning support
//
struct _DEVICE_EXTENSION *NextPartition;
//
// Drive parameters returned in IO device control.
//
DISK_GEOMETRY DiskGeometry;
//
// Number of hidden sectors for BPB.
//
ULONG HiddenSectors;
//
// Length of partition in bytes
//
LARGE_INTEGER PartitionLength;
//
// Number of bytes before start of partition
//
LARGE_INTEGER StartingOffset;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION)
#define BYTES_PER_SECTOR 512
#define SECTOR_SHIFT 9
#define NUMBER_MCA_ADAPTERS 2
#define HIGHEST_MEMORY_ADDRESS (16 * 1024 * 1024)
ULONG AbiosDiskErrorLogSequence = 0;
//
// Set debug macro.
//
#if DBG
#define DebugPrint(x) AbiosDebugPrint x
#else
#define DebugPrint(x)
#endif // DBG
//
// Function declarations
//
NTSTATUS
DriverEntry(
IN PVOID DriverObject,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
AbiosProcessLogicalId(
IN PDRIVER_OBJECT DriverObject,
IN USHORT LogicalId,
IN PCONFIGURATION_INFORMATION ConfigurationInformation,
IN USHORT Selector
);
NTSTATUS
AbiosProcessDisk(
IN PDRIVER_OBJECT DriverObject,
IN PCONTROLLER_OBJECT ControllerObject,
IN PCONFIGURATION_INFORMATION ConfigurationInformation,
IN USHORT LogicalId,
IN USHORT Unit
);
NTSTATUS
AbiosDiskCreate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
AbiosDiskReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
AbiosDiskStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
IO_ALLOCATION_ACTION
AbiosDiskControllerPioAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
IO_ALLOCATION_ACTION
AbiosDiskControllerForDmaAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
IO_ALLOCATION_ACTION
AbiosDiskAdapterAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
VOID
AbiosBuildRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp
);
BOOLEAN
AbiosDiskSynchronizedIoStart(
IN PVOID DeviceObject
);
BOOLEAN
AbiosDiskInterrupt(
IN PKINTERRUPT InterruptObject,
IN PVOID Context
);
VOID
AbiosDiskCompletionDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
AbiosDiskTimeoutDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
BOOLEAN
AbiosCheckReturnCode(
IN PVOID DeviceObject
);
NTSTATUS
AbiosInterpretReturnCode(
IN PDEVICE_EXTENSION DeviceExtension,
IN PABIOS_REQUEST_BLOCK RequestBlock
);
NTSTATUS
AbiosDiskDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
AbiosDiskLogError(
IN PDEVICE_EXTENSION DeviceExtension,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIoStatus
);
VOID
UpdateDeviceObjects(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
//
// Define code that may be discarded after boot.
//
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, AbiosProcessDisk)
#pragma alloc_text(INIT, AbiosProcessLogicalId)
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Initialize ABIOS driver.
This return is the system initialization entry point when
the driver is linked into the kernel.
Arguments:
DriverObject - for the ABIOS driver.
Return Value:
NTSTATUS
--*/
{
USHORT relativeLid = 1;
NTSTATUS returnStatus = STATUS_NO_SUCH_DEVICE;
USHORT logicalId;
PCONFIGURATION_INFORMATION configurationInformation;
USHORT selectorArray[NUMBER_MCA_ADAPTERS];
NTSTATUS status;
ULONG i;
//
// Get the configuration information for this driver.
//
configurationInformation = IoGetConfigurationInformation();
//
// Set up the device driver entry points.
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = AbiosDiskCreate;
DriverObject->DriverStartIo = AbiosDiskStartIo;
DriverObject->MajorFunction[IRP_MJ_READ] = AbiosDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = AbiosDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AbiosDiskDeviceControl;
//
// Allocate 16:16 selectors for request blocks.
// NOTE: This call is the first ABIOS call and determines
// the presence of ABIOS. If it fails then the driver fails
// to initialize and returns the status of this call.
//
status = KeI386AllocateGdtSelectors(selectorArray, NUMBER_MCA_ADAPTERS);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"AbiosDiskInitialize: KeI386AllocateGdtSelectors failed\n"));
return status;
}
//
// Find MCA adapters.
//
for (i = 0; i < NUMBER_MCA_ADAPTERS; i++) {
status = KeI386GetLid(ABIOS_DEVICE_DISK,
relativeLid,
FALSE,
DriverObject,
&logicalId);
if (NT_SUCCESS(status)) {
//
// Process this disk.
//
status = AbiosProcessLogicalId(DriverObject,
logicalId,
configurationInformation,
selectorArray[relativeLid-1]);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosDiskInitialize: LID initialization failed\n"));
} else {
returnStatus = STATUS_SUCCESS;
}
}
//
// Increment relative LID;
//
relativeLid++;
}
if (!NT_SUCCESS(returnStatus) &&
status == STATUS_ABIOS_NOT_PRESENT) {
KeI386ReleaseGdtSelectors(selectorArray, NUMBER_MCA_ADAPTERS);
}
return returnStatus;
} // end DriverEntry()
NTSTATUS
AbiosProcessLogicalId(
IN PDRIVER_OBJECT DriverObject,
IN USHORT LogicalId,
IN PCONFIGURATION_INFORMATION ConfigurationInformation,
IN USHORT Selector
)
/*++
Routine Description:
Create controller object to represent this logical id.
Then allocate ABIOS request block and create GDT selector
entry to point to it. Issue 'Get Logical ID Parameters' to
find the number of disk devices and IRQ information.
Using this information set up the interrupt, DPC and timer.
Then for each disk call AbiosProcessDisk to create device
objects and initialize device extension to describe the
disk.
Arguments:
DriverObject - for the ABIOS Driver.
LogicalId - the logical id for ABIOS indicating what to process.
ConfigurationInformation - pointer to the system configuration structure.
Selector - selector allocated to frame the ABIOS request block.
This routine will allocate the space and initialize
this selector entry in the GDT.
Return Value:
NTSTATUS
--*/
{
ULONG disksOnLid = 0;
PABIOS_REQUEST_BLOCK requestBlock;
PABIOS_LID_PARAMETERS lidParameters;
PCONTROLLER_OBJECT controllerObject;
DEVICE_DESCRIPTION deviceDescription;
PCONTROLLER_EXTENSION controllerExtension;
KIRQL irql;
NTSTATUS status;
USHORT unit;
KAFFINITY affinity;
ULONG vector;
PABIOS_DISK_READ_DEVICE_PARMS diskParameters;
//
// Create controller object for this logical unit.
//
controllerObject = IoCreateController(sizeof(CONTROLLER_EXTENSION));
if (controllerObject == NULL) {
return STATUS_NO_MEMORY;
}
controllerExtension = (PCONTROLLER_EXTENSION)(controllerObject + 1);
controllerExtension->ControllerObject = controllerObject;
//
// Store driver object in controller extension to be
// used as driver unique identifier to claim ABIOS LIDs.
//
controllerExtension->DriverObject = DriverObject;
//
// Allocate ABIOS request block for this device.
//
requestBlock = controllerExtension->RequestBlock =
ExAllocatePool(NonPagedPool,
2 * REQUEST_BLOCK_LENGTH);
if (!requestBlock) {
IoDeleteController(controllerObject);
return STATUS_NO_MEMORY;
}
controllerExtension->RetryRequestBlock =
(PABIOS_REQUEST_BLOCK)((PUCHAR)requestBlock + REQUEST_BLOCK_LENGTH);
//
// Initialize request block to zero.
//
RtlZeroMemory(requestBlock, REQUEST_BLOCK_LENGTH);
RtlZeroMemory(controllerExtension->RetryRequestBlock, REQUEST_BLOCK_LENGTH);
//
// Fill in static fields.
//
requestBlock->Length = REQUEST_BLOCK_LENGTH;
requestBlock->LogicalId = LogicalId;
requestBlock->Unit = 0;
//
// Create GDT selector for request block.
//
KeI386FlatToGdtSelector((ULONG)requestBlock,
REQUEST_BLOCK_LENGTH,
Selector);
//
// Create 16:0 virtual address of request block.
//
controllerExtension->Selector = (PUCHAR)(Selector<<16);
//
// Issue Return Logical ID Parameters function.
//
requestBlock->Length = 0x0020;
requestBlock->Function = ABIOS_FUNCTION_GET_LID_PARMS;
status = KeI386AbiosCall(LogicalId,
DriverObject,
(PUCHAR)(Selector<<16),
ABIOS_START_ROUTINE);
if (!NT_SUCCESS(status)) {
ExFreePool(requestBlock);
IoDeleteController(controllerObject);
DebugPrint((1,"AbiosProcessLogicalId: KeI386ABiosCall Failed\n"));
return status;
}
//
// Cast requestBlock to return parameters.
//
lidParameters = (PABIOS_LID_PARAMETERS)requestBlock;
if ((lidParameters->LidFlags & ABIOS_DATA_TRANSFER_DATA_MASK) == ABIOS_DATA_LOGICAL) {
//
// Create GDT selector for data pointers.
//
status = KeI386AllocateGdtSelectors(&controllerExtension->DataSelector, 1);
if (!NT_SUCCESS(status)) {
ExFreePool(requestBlock);
IoDeleteController(controllerObject);
DebugPrint((1, "AbiosProcessLogicalId: Failed data selector\n"));
return status;
}
} else {
controllerExtension->DataSelector = 0;
}
//
// Copy results to controller extension.
//
controllerExtension->LogicalId = LogicalId;
controllerExtension->Irq = lidParameters->Irq;
controllerExtension->UnitCount = lidParameters->UnitCount;
controllerExtension->ArbitrationLevel = lidParameters->ArbitrationLevel;
DebugPrint((2,
"AbiosProcessLogicalId: Irq is %d\n",
lidParameters->Irq));
DebugPrint((2,
"AbiosProcessLogicalId: Unit count is %d\n",
lidParameters->UnitCount));
DebugPrint((2,
"AbiosProcessLogicalId: Use physical data addresses\n"));
controllerExtension->PerformingReset = FALSE;
//
// Assume request block length is less than or equal
// to the size of the request block allocated.
//
ASSERT(lidParameters->RequestBlockLength <= REQUEST_BLOCK_LENGTH);
controllerExtension->RequestBlockLength = lidParameters->RequestBlockLength;
//
// Check the drives attached to this LogicalId to verify that ABIOS
// support is needed.
//
disksOnLid = controllerExtension->UnitCount;
diskParameters = (PABIOS_DISK_READ_DEVICE_PARMS) requestBlock;
for (unit = 0; unit < controllerExtension->UnitCount; unit++) {
//
// Issue ABIOS Read Device Parameters.
//
requestBlock->Length = controllerExtension->RequestBlockLength;
requestBlock->LogicalId = controllerExtension->LogicalId;
requestBlock->Unit = unit;
requestBlock->Function = ABIOS_FUNCTION_READ_DEVICE_PARMS;
status = KeI386AbiosCall(controllerExtension->LogicalId,
DriverObject,
controllerExtension->Selector,
ABIOS_START_ROUTINE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosProcessLogicalId: Can't read device parameters\n"));
return status;
}
//
// Check to see it the disk device is a SCSI.
//
if (diskParameters->DeviceControlFlags & DISK_READ_PARMS_SCSI_DEVICE) {
DebugPrint((2, "AbiosProcessLogicalId: This is a SCSI device\n"));
disksOnLid--;
continue;
}
//
// Check if this device supports SCBs.
//
if (diskParameters->DeviceControlFlags & DISK_READ_PARMS_SUPPORTS_SCB) {
DebugPrint((2, "AbiosProcessLogicalId: This device supports SCBs\n"));
disksOnLid--;
continue;
}
}
if (!disksOnLid) {
//
// There are no devices requiring ABIOS support.
//
return STATUS_UNSUCCESSFUL;
}
//
// Initialize completion DPC routine.
//
KeInitializeDpc(&controllerExtension->CompletionDpc,
AbiosDiskCompletionDpc,
controllerExtension);
//
// Initialize timer.
//
KeInitializeTimer(&controllerExtension->Timer);
KeInitializeDpc(&controllerExtension->TimerDpc,
AbiosDiskTimeoutDpc,
controllerExtension);
//
// Call HAL to get system interrupt parameters.
//
vector = HalGetInterruptVector(MicroChannel, // InterfaceType
0, // BusNumber
controllerExtension->Irq,
0, // BusInterruptVector
&irql,
&affinity);
//
// Allocate and connect interrupt objects for this device to all of the
// processors on which this device can interrupt.
//
controllerExtension->DmaAdapterObject = NULL;
status = IoConnectInterrupt(&controllerExtension->InterruptObject,
AbiosDiskInterrupt,
controllerExtension,
(PKSPIN_LOCK)NULL,
vector,
irql,
irql,
LevelSensitive,
TRUE,
affinity,
FALSE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosProcessLogicalId: Can't connect to Interrupt %d\n",
controllerExtension->Irq));
IoDeleteController(controllerObject);
ExFreePool(requestBlock);
(VOID)KeI386ReleaseLid(LogicalId, DriverObject);
return status;
}
//
// Allocate an adapter for this controller.
//
RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
deviceDescription.InterfaceType = MicroChannel;
deviceDescription.BusNumber = 0;
deviceDescription.ScatterGather = FALSE;
deviceDescription.Master = TRUE;
deviceDescription.Dma32BitAddresses = FALSE;
deviceDescription.AutoInitialize = FALSE;
deviceDescription.MaximumLength = MM_MAXIMUM_DISK_IO_SIZE + PAGE_SIZE;
controllerExtension->DmaAdapterObject = HalGetAdapter(
&deviceDescription,
&controllerExtension->MaximumNumberOfPages
);
if (controllerExtension->DmaAdapterObject == NULL) {
DebugPrint((1,"AbiosProcessLogicalId: Can't get DMA adapter object\n"));
ExFreePool(controllerExtension);
ExFreePool(requestBlock);
(VOID)KeI386ReleaseLid(LogicalId, DriverObject);
return status;
}
//
// Initialize request timeout value.
//
controllerExtension->TimeoutValue.LowPart = (ULONG)
-(10 * 1000 * 1000 * ABIOS_DISK_TIMEOUT);
controllerExtension->TimeoutValue.HighPart = -1;
//
// Process each disk on controller.
//
for (unit = 0; unit < controllerExtension->UnitCount; unit++) {
status = AbiosProcessDisk(DriverObject,
controllerObject,
ConfigurationInformation,
LogicalId,
unit);
if (NT_SUCCESS(status)) {
//
// Increment system disk count.
//
ConfigurationInformation->DiskCount++;
disksOnLid++;
} else {
DebugPrint((1,
"AbiosProcessLogicalId: Disk %d failed to initialize\n",
unit));
}
}
//
// If no disks initialized then fail.
//
if (disksOnLid) {
return STATUS_SUCCESS;
} else {
//
// Clean up structures no longer needed.
//
IoDisconnectInterrupt(controllerExtension->InterruptObject);
ExFreePool(requestBlock);
IoDeleteController(controllerObject);
return STATUS_UNSUCCESSFUL;
}
} // end AbiosProcessLogicalId()
NTSTATUS
AbiosProcessDisk(
IN PDRIVER_OBJECT DriverObject,
IN PCONTROLLER_OBJECT ControllerObject,
IN PCONFIGURATION_INFORMATION ConfigurationInformation,
IN USHORT LogicalId,
IN USHORT Unit
)
/*++
Routine Description:
Given a logical id and a unit number for ABIOS, this routine will
process the disk and create all device objects for the partitions
contained on the disk.
Arguments:
DriverObject - the system driver object for ABIOSDISK.
ControllerObject - the controller object created for this ABIOS controller.
ConfigurationInformation - pointer to the system configuration structure.
LogicalId - the logical id for ABIOS indicating what to process.
Unit - the ABIOS unit number for the disk to process.
Return Value:
NTSTATUS
--*/
{
UCHAR ntNameBuffer[256];
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
STRING ntNameString;
UNICODE_STRING ntUnicodeString;
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_EXTENSION physicalDevice;
PDRIVE_LAYOUT_INFORMATION partitionList;
USHORT partitionNumber;
PCONTROLLER_EXTENSION controllerExtension =
ControllerObject->ControllerExtension;
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
PULONG diskCount = &ConfigurationInformation->DiskCount;
PABIOS_DISK_READ_DEVICE_PARMS diskParameters =
(PABIOS_DISK_READ_DEVICE_PARMS)requestBlock;
//
// Issue ABIOS Read Device Parameters.
//
requestBlock->Length = controllerExtension->RequestBlockLength;
requestBlock->LogicalId = controllerExtension->LogicalId;
requestBlock->Unit = Unit;
requestBlock->Function = ABIOS_FUNCTION_READ_DEVICE_PARMS;
status = KeI386AbiosCall(controllerExtension->LogicalId,
DriverObject,
controllerExtension->Selector,
ABIOS_START_ROUTINE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosProcessDisk: Can't read device parameters\n"));
return status;
}
//
// Check to see it the disk device is a SCSI.
//
if (diskParameters->DeviceControlFlags & DISK_READ_PARMS_SCSI_DEVICE) {
DebugPrint((2, "AbiosProcessDisk: This is a SCSI device\n"));
return STATUS_UNSUCCESSFUL;
}
//
// Check if this device supports SCBs.
//
if (diskParameters->DeviceControlFlags & DISK_READ_PARMS_SUPPORTS_SCB) {
DebugPrint((2, "AbiosProcessDisk: This device supports SCBs\n"));
return STATUS_UNSUCCESSFUL;
}
if (diskParameters->DeviceControlFlags & DISK_READ_PARMS_ST506) {
DebugPrint((2,
"AbiosProcessDisk: This device supports the ST506 interface\n"));
}
//
// Set up an object directory to contain the objects for this
// device and all its partitions.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d",
*diskCount);
RtlInitString(&ntNameString,
ntNameBuffer);
(VOID)RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
InitializeObjectAttributes(&objectAttributes,
&ntUnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
NULL);
status = ZwCreateDirectoryObject(&handle,
DIRECTORY_ALL_ACCESS,
&objectAttributes);
RtlFreeUnicodeString(&ntUnicodeString);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"Could not create directory %s\n",
ntNameBuffer));
return status;
}
//
// Create device object for this device. Each device will
// have at least one device object. The required device object
// describes the entire device. Its directory path is
// \Device\HarddiskN/Partition0, where N = device number.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d\\Partition0",
*diskCount);
RtlInitString(&ntNameString,
ntNameBuffer);
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
ASSERT(NT_SUCCESS(status));
//
// Create physical device object.
//
status = IoCreateDevice(DriverObject,
DEVICE_EXTENSION_SIZE,
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"CreateDeviceObjects: Can not create device %s\n",
ntNameBuffer));
//
// Delete directory by making it tempory and closing the
// only reference to the directory and return.
//
ZwMakeTemporaryObject(handle);
ZwClose(handle);
RtlFreeUnicodeString(&ntUnicodeString);
return status;
}
//
// A device object was created. Close the handle to the directory.
//
ZwClose(handle);
RtlFreeUnicodeString(&ntUnicodeString);
//
// initialize the new deviceObject.
//
deviceObject->Flags |= DO_DIRECT_IO;
deviceObject->AlignmentRequirement = BYTES_PER_SECTOR - 1;
deviceObject->StackSize = 1;
//
// Initialize the device extension
//
deviceExtension = deviceObject->DeviceExtension;
deviceExtension->DeviceObject = deviceObject;
deviceExtension = deviceObject->DeviceExtension;
deviceExtension->ControllerExtension = controllerExtension;
deviceExtension->Unit = Unit;
deviceExtension->DiskNumber = *diskCount;
deviceExtension->NextPartition = NULL;
//
// Physical device object will describe the entire
// device, starting at byte offset 0.
//
deviceExtension->StartingOffset.QuadPart = 0;
//
// Copy results of ABIOS Read Device Parameter call.
//
ASSERT(diskParameters->SectorSize == 2);
//
// ABIOS only handles sectors sizes of 512.
//
deviceExtension->DiskGeometry.BytesPerSector = BYTES_PER_SECTOR;
ASSERT(diskParameters->SectorSize == 2);
deviceExtension->DiskGeometry.SectorsPerTrack =
diskParameters->SectorsPerTrack;
deviceExtension->DiskGeometry.MediaType = FixedMedia;
deviceExtension->SectorShift = SECTOR_SHIFT;
deviceExtension->SectorsInPage = PAGE_SIZE >> SECTOR_SHIFT;
deviceExtension->DiskGeometry.TracksPerCylinder =
diskParameters->NumberHeads;
deviceExtension->DiskGeometry.Cylinders.QuadPart =
diskParameters->NumberCylinders;
if (diskParameters->SoftwareRetryCount <= 1) {
//
// Don't accept a single retry. This code will retry up to 2 times
// without a reset, then attempt a reset/retry.
//
deviceExtension->SoftwareRetries = 3;
} else {
//
// This could be less than the default or much greater.
// Accept what the controller suggests.
//
deviceExtension->SoftwareRetries = diskParameters->SoftwareRetryCount;
}
deviceExtension->MaximumNumberBlocks = diskParameters->MaximumNumberBlocks;
//
// Limit the number of map registers to the maximum number of blocks so
// that extra mapped registers are never allocated.
//
if ((ULONG)(deviceExtension->MaximumNumberBlocks / deviceExtension->SectorsInPage) <
controllerExtension->MaximumNumberOfPages) {
controllerExtension->MaximumNumberOfPages = 1 +
deviceExtension->MaximumNumberBlocks / deviceExtension->SectorsInPage;
}
//
// Calculate size of physical disk. This is set to the maximum
// signed long value to encompass all disks.
//
deviceExtension->PartitionLength.LowPart = (ULONG) -1;
deviceExtension->PartitionLength.HighPart = 0x7fffffff;
//
// Set physical device pointer to this device extension.
//
physicalDevice = deviceExtension->PhysicalDevice = deviceExtension;
//
// Create objects for all the partitions on the device.
//
status = IoReadPartitionTable(deviceObject,
deviceExtension->DiskGeometry.BytesPerSector,
TRUE,
(PVOID)&partitionList);
if (NT_SUCCESS(status)) {
DebugPrint((2,
"AbiosProcessDisk: Number of partitions is %d\n",
partitionList->PartitionCount));
//
// Create device objects for the device partitions (if any).
// PartitionCount includes physical device partition 0,
// so only one partition means no objects to create.
//
for (partitionNumber = 0; partitionNumber <
partitionList->PartitionCount; partitionNumber++) {
//
// Create partition object and set up partition parameters.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d\\Partition%d",
*diskCount,
partitionNumber + 1);
RtlInitString(&ntNameString,
ntNameBuffer);
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
ASSERT(NT_SUCCESS(status));
status = IoCreateDevice(DriverObject,
DEVICE_EXTENSION_SIZE,
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"CreateDeviceObjects: Can't create device object for %s, %x\n",
ntNameBuffer,
status));
RtlFreeUnicodeString(&ntUnicodeString);
return status;
} else {
DebugPrint((2,
"AbiosProcessDisk: created device object for %s\n",
ntNameBuffer));
}
RtlFreeUnicodeString(&ntUnicodeString);
//
// Initialize device object.
//
deviceObject->Flags |= DO_DIRECT_IO;
deviceObject->AlignmentRequirement = BYTES_PER_SECTOR - 1;
deviceObject->StackSize = 1;
//
// Link partition extensions to support dynamic partitioning.
//
deviceExtension->NextPartition = deviceObject->DeviceExtension;
//
// initialize device extension.
//
deviceExtension = deviceObject->DeviceExtension;
deviceExtension->PhysicalDevice = physicalDevice;
deviceExtension->ControllerExtension = controllerExtension;
deviceExtension->PartitionNumber = partitionNumber + 1;
deviceExtension->PartitionType =
partitionList->PartitionEntry[partitionNumber].PartitionType;
deviceExtension->BootIndicator =
partitionList->PartitionEntry[partitionNumber].BootIndicator;
deviceExtension->StartingOffset =
partitionList->PartitionEntry[partitionNumber].StartingOffset;
deviceExtension->PartitionLength =
partitionList->PartitionEntry[partitionNumber].PartitionLength;
deviceExtension->HiddenSectors =
partitionList->PartitionEntry[partitionNumber].HiddenSectors;
deviceExtension->DiskGeometry.BytesPerSector = BYTES_PER_SECTOR;
deviceExtension->DiskGeometry.MediaType = FixedMedia;
deviceExtension->SectorShift = SECTOR_SHIFT;
deviceExtension->DiskNumber = *diskCount;
deviceExtension->NextPartition = NULL;
deviceExtension->DeviceObject = deviceObject;
} // end for partitionNumber ...
} else {
DebugPrint((1,"CreateDeviceObjects: IoReadPartitionTable failed\n"));
} // end if...else
return STATUS_SUCCESS;
} // end AbiosProcessDisk()
NTSTATUS
AbiosDiskCreate(
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 - representing the device being opened.
IRP - the open/create request IRP.
Return Value:
NTSTATUS
--*/
{
DebugPrint((3,"AbiosDiskCreate: Enter routine\n"));
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
} // end AbiosDiskCreate()
NTSTATUS
AbiosDiskReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the driver entry point for read and write requests
to ABIOS disks. IRP parameters are verified and relative
block address is calculated for request to be queued. If
queue is empty, request is started via a call to AbiosDiskStartIo.
Arguments:
DeviceObject - representing the device being referenced.
IRP - the read/write request IRP.
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG transferByteCount = irpStack->Parameters.Read.Length;
LARGE_INTEGER startingOffset = irpStack->Parameters.Read.ByteOffset;
ULONG sectorOffset;
//
// Verify parameters of this request.
// Check that ending sector is within partition and
// that number of bytes to transfer is a multiple of
// the sector size.
//
if (((deviceExtension->PartitionLength.QuadPart - startingOffset.QuadPart) <
transferByteCount) ||
(transferByteCount % deviceExtension->DiskGeometry.BytesPerSector)) {
DebugPrint((1,"AbiosDiskReadWrite: Invalid I/O parameters\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
//
// Zero out information field in status block.
// Bytes transferred will be returned here.
//
Irp->IoStatus.Information = 0;
//
// Mark IRP with status pending.
//
IoMarkIrpPending(Irp);
//
// Add partition byte offset to make start byte relative to
// beginning of disk.
//
irpStack->Parameters.Read.ByteOffset.QuadPart = startingOffset.QuadPart +
deviceExtension->StartingOffset.QuadPart;
//
// Calculate sector offset for queue sort.
//
sectorOffset = (ULONG) (irpStack->Parameters.Read.ByteOffset.QuadPart >>
deviceExtension->SectorShift);
//
// Queue or start IRP.
//
IoStartPacket(deviceExtension->PhysicalDevice->DeviceObject,
Irp,
&sectorOffset,
NULL);
return STATUS_PENDING;
} // end AbiosDiskReadWrite()
VOID
AbiosDiskStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Start io packet via a routine called when the controller object
can be allocated.
Arguments:
DeviceObject - the device being referenced.
Irp - represents the request to be started.
Return Value:
None
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
UNREFERENCED_PARAMETER(Irp);
if (controllerExtension->DataSelector) {
IoAllocateController(controllerExtension->ControllerObject,
DeviceObject,
(PDRIVER_CONTROL)AbiosDiskControllerPioAllocated,
NULL);
return;
}
//
// Allocate controller object.
//
IoAllocateController(controllerExtension->ControllerObject,
DeviceObject,
(PDRIVER_CONTROL)AbiosDiskControllerForDmaAllocated,
NULL);
return;
} // end AbiosDiskStartIo()
IO_ALLOCATION_ACTION
AbiosDiskControllerForDmaAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
Calls IoAllocateAdapterChannel to get map registers.
Arguments:
DeviceObject - the device being referenced.
Irp - the i/o request.
MapRegisterBase - not used.
Context - not used.
Return Value:
IO_ALLOCATION_ACTION
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
ULONG numberOfPages;
//
// Calculate the number of pages required by the transfer.
//
if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
//
// Assumed to be a VERIFY request.
//
numberOfPages = 0;
} else {
numberOfPages = BYTES_TO_PAGES(Irp->MdlAddress->ByteOffset +
irpStack->Parameters.Read.Length);
}
//
// Limit the number of pages to that allowed by the adapter object.
//
if (controllerExtension->MaximumNumberOfPages < numberOfPages) {
numberOfPages = controllerExtension->MaximumNumberOfPages;
}
controllerExtension->NumberOfMapRegisters = numberOfPages;
IoAllocateAdapterChannel(controllerExtension->DmaAdapterObject,
DeviceObject,
numberOfPages,
AbiosDiskAdapterAllocated,
NULL );
//
// Keep the controller object.
//
return KeepObject;
} // AbiosDiskControllerForDmaAllocated()
IO_ALLOCATION_ACTION
AbiosDiskAdapterAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
Build ABIOS request block and call synchronized routine to call ABIOS.
Arguments:
DeviceObject - the device being referenced.
Irp - the i/o request.
MapRegisterBase - the used for IoMapTransfer
Context - unused.
Return Value:
IO_ALLOCATION_ACTION
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
//
// Debug: check if controller object really free.
//
ASSERT(!controllerExtension->IrpAddress);
//
// Move IRP address and device object to controller extension.
//
controllerExtension->IrpAddress = Irp;
controllerExtension->DeviceObject = DeviceObject;
controllerExtension->MapRegisterBase = MapRegisterBase;
//
// Calculate first sector and count. Partition byte offset already
// added in to first sector.
//
controllerExtension->StartingSector = (ULONG)
(irpStack->Parameters.Read.ByteOffset.QuadPart >>
deviceExtension->SectorShift);
controllerExtension->RemainingSectors = controllerExtension->TotalSectors =
irpStack->Parameters.Read.Length >>
deviceExtension->SectorShift;
//
// Initialize transferred value and retries.
//
controllerExtension->BytesTransferred = 0;
controllerExtension->RetryCount = deviceExtension->SoftwareRetries;
if (irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
//
// Determine virtual memory address for this transfer.
//
controllerExtension->VirtualAddress =
MmGetMdlVirtualAddress(Irp->MdlAddress);
} else {
controllerExtension->VirtualAddress = 0;
}
//
// Build ABIOS request block.
//
AbiosBuildRequest(deviceExtension, Irp);
//
// Start timing request.
//
KeSetTimer(&controllerExtension->Timer,
controllerExtension->TimeoutValue,
&controllerExtension->TimerDpc);
//
// Call synchronized routine to submit request block.
//
KeSynchronizeExecution(
deviceExtension->ControllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
DeviceObject);
//
// Free the DMA adapter object but keep the map registers.
//
return DeallocateObjectKeepRegisters;
} // end AbiosDiskAdapterAllocated()
IO_ALLOCATION_ACTION
AbiosDiskControllerPioAllocated(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
Build ABIOS request block and call synchronized routine to call ABIOS.
Arguments:
DeviceObject - the device being referenced.
Irp - the i/o request.
MapRegisterBase - the used for IoMapTransfer
Context - unused.
Return Value:
IO_ALLOCATION_ACTION
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
//
// Move IRP address and device object to controller extension.
//
controllerExtension->IrpAddress = Irp;
controllerExtension->DeviceObject = DeviceObject;
controllerExtension->MapRegisterBase = MapRegisterBase;
//
// Calculate first sector and count. Partition byte offset already
// added in to first sector.
//
controllerExtension->StartingSector = (ULONG)
(irpStack->Parameters.Read.ByteOffset.QuadPart >>
deviceExtension->SectorShift);
controllerExtension->RemainingSectors = controllerExtension->TotalSectors =
irpStack->Parameters.Read.Length >>
deviceExtension->SectorShift;
//
// Initialize transferred value and retries.
//
controllerExtension->BytesTransferred = 0;
controllerExtension->RetryCount = deviceExtension->SoftwareRetries;
if (irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
//
// Determine virtual memory address for this transfer and set up
// the system address for the PIO.
//
controllerExtension->VirtualAddress =
MmGetMdlVirtualAddress(Irp->MdlAddress);
controllerExtension->SystemAddress =
MmGetSystemAddressForMdl(Irp->MdlAddress);
} else {
controllerExtension->VirtualAddress = 0;
}
//
// Build ABIOS request block.
//
AbiosBuildRequest(deviceExtension, Irp);
//
// Start timing request.
//
KeSetTimer(&controllerExtension->Timer,
controllerExtension->TimeoutValue,
&controllerExtension->TimerDpc);
//
// Call synchronized routine to submit request block.
//
KeSynchronizeExecution(
deviceExtension->ControllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
DeviceObject);
return KeepObject;
} // AbiosDiskControllerPioAllocated()
VOID
AbiosBuildRequest(
IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP Irp
)
/*++
Routine Description:
Build ABIOS request block for read, write or verify. The request block is
located via the controller extension in the device extension.
In the case of verify, the parameters for the offset and length of the
verify have been stored in a "Read" parameter block of the Irp stack.
Note: Read and Write parameter blocks are identical.
Arguments:
DeviceExtension - The device for the read or write.
Irp - The IRP containing the VERIFY request.
Return Value:
None
--*/
{
PCONTROLLER_EXTENSION controllerExtension =
DeviceExtension->ControllerExtension;
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG length;
//
// Determine if sectors requested are greater than maximum
// sector count for a single ABIOS request.
//
if (controllerExtension->RemainingSectors >
DeviceExtension->MaximumNumberBlocks) {
//
// Set up sector count to maximum number of
// sectors that can be transferred.
//
controllerExtension->TransferSectors =
DeviceExtension->MaximumNumberBlocks;
} else {
controllerExtension->TransferSectors =
controllerExtension->RemainingSectors;
}
//
// Zero out request block.
//
RtlZeroMemory(requestBlock, controllerExtension->RequestBlockLength);
if (irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
//
// Determine ABIOS command.
//
if (irpStack->MajorFunction == IRP_MJ_READ) {
requestBlock->Function = ABIOS_FUNCTION_READ;
} else {
requestBlock->Function = ABIOS_FUNCTION_WRITE;
}
if (controllerExtension->DataSelector) {
//
// Limit the transfer to 64K
//
if (controllerExtension->TransferSectors > ((64 * 1024) / BYTES_PER_SECTOR)) {
controllerExtension->TransferSectors = ((64 * 1024) / BYTES_PER_SECTOR);
length = (64 * 1024) >> DeviceExtension->SectorShift;
} else {
length = controllerExtension->TransferSectors >>
DeviceExtension->SectorShift;
}
//
// Set up the data selector to frame the data buffer.
//
KeI386FlatToGdtSelector((ULONG)controllerExtension->SystemAddress,
(USHORT)(64 * 1024),
controllerExtension->DataSelector);
requestBlock->DataSelector = controllerExtension->DataSelector;
} else {
//
// Limit the request to the number of map registers available.
//
length = controllerExtension->MaximumNumberOfPages << PAGE_SHIFT;
length -= (ULONG) controllerExtension->VirtualAddress & (PAGE_SIZE - 1);
//
// Truncate to sectors.
//
length = length >> DeviceExtension->SectorShift;
//
// Shorten the transfer if necessary.
//
if (length < controllerExtension->TransferSectors ) {
controllerExtension->TransferSectors = length;
} else {
length = controllerExtension->TransferSectors;
}
//
// Convert length from sectors to bytes.
//
length <<= DeviceExtension->SectorShift;
//
// Map the transfer for the adapter.
//
controllerExtension->PhysicalAddress =
IoMapTransfer(controllerExtension->DmaAdapterObject,
Irp->MdlAddress,
controllerExtension->MapRegisterBase,
controllerExtension->VirtualAddress,
&length,
(BOOLEAN)
(irpStack->MajorFunction == IRP_MJ_READ ? FALSE : TRUE)
).LowPart;
}
} else {
requestBlock->Function = ABIOS_FUNCTION_VERIFY;
}
//
// Construct the request block.
//
requestBlock->PhysicalAddress = controllerExtension->PhysicalAddress;
requestBlock->Length = controllerExtension->RequestBlockLength;
requestBlock->LogicalId = controllerExtension->LogicalId;
requestBlock->Unit = DeviceExtension->Unit;
requestBlock->BlockCount = (USHORT) controllerExtension->TransferSectors;
requestBlock->RelativeBlockAddress = controllerExtension->StartingSector;
requestBlock->CachingByte = 0;
DebugPrint((4,
"AbiosBuildRequest: %s sector %d to 0x%x for %d\n",
(requestBlock->Function == ABIOS_FUNCTION_READ) ?
"Read" : "Write",
requestBlock->RelativeBlockAddress,
requestBlock->PhysicalAddress,
requestBlock->BlockCount));
} // end AbiosBuildRequest()
BOOLEAN
AbiosDiskSynchronizedIoStart(
IN PVOID DeviceObject
)
/*++
Routine Description:
This routine is called from AbiosDiskStartIo and is synchronized
with the interrupt service routine. The ABIOS request block is
complete and ready to be submitted to the ABIOS.
It is also called from AbiosDiskTickHandler when a request times
out and AbiosDiskCompletionDpc. In these cases, it is to retry a
request.
Arguments:
DeviceObject - representing the device to start.
Return Value:
Status
--*/
{
PDEVICE_EXTENSION deviceExtension =
((PDEVICE_OBJECT) DeviceObject)->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
PIRP irp = controllerExtension->IrpAddress;
NTSTATUS status;
//
// Initialize return code to not valid.
//
requestBlock->ReturnCode = ABIOS_RC_NOT_VALID;
if (requestBlock->Function != ABIOS_FUNCTION_RESET_DEVICE) {
//
// Save a copy of the request block for potential retries.
//
RtlMoveMemory(controllerExtension->RetryRequestBlock,
requestBlock,
REQUEST_BLOCK_LENGTH);
}
//
// Call ABIOS to submit request block.
//
status = KeI386AbiosCall(controllerExtension->LogicalId,
controllerExtension->DriverObject,
controllerExtension->Selector,
ABIOS_START_ROUTINE);
if ((!NT_SUCCESS(status)) || (requestBlock->ReturnCode & 0x8000)) {
DebugPrint((1,"AbiosDiskSynchronizedIoStart: ABIOS call failed:\n"));
DebugPrint((1,"\tStatus %lx; ABIOS return code %lx\n",
status,
requestBlock->ReturnCode));
//
// Do not complete request. Let it time out and be completed from
// the timer DPC. Can't complete request or cancel timer at raised
// IRQL.
//
return FALSE;
}
(VOID)AbiosCheckReturnCode((PVOID)DeviceObject);
return TRUE;
} // end AbiosDiskSynchronizedIoStart()
BOOLEAN
AbiosDiskInterrupt(
IN PKINTERRUPT InterruptObject,
IN PVOID Context
)
/*++
Routine Description:
This is the interrupt service routine (ISR) for the ABIOS disk. It is
called by the interrupt manager to handle a disk device interrupt.
Arguments:
InterruptObject - not used.
Context - pointer to the ControllerExtension that interrupted.
Return Value:
TRUE if the interrupt is a device this driver services.
--*/
{
PCONTROLLER_EXTENSION controllerExtension = Context;
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
NTSTATUS status;
UNREFERENCED_PARAMETER(InterruptObject);
//
// Issue ABIOS call to verify interrupt and clear device
// interrupt.
//
status = KeI386AbiosCall(controllerExtension->LogicalId,
controllerExtension->DriverObject,
controllerExtension->Selector,
ABIOS_INTERRUPT_ROUTINE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosDiskInterrupt: Abios call failed\n"));
return FALSE;
}
if (!controllerExtension->DmaAdapterObject) {
//
// Interrupt occurred during initialization.
//
return TRUE;
}
return AbiosCheckReturnCode((PVOID)controllerExtension->DeviceObject);
} // end AbiosDiskInterrupt()
VOID
AbiosDiskCompletionDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine is called to complete processing after an interrupt.
It will check the ABIOS status and determine if the request failed
or needs to be restarted. On success it checks the parameters for
the request to see if additional processing is required to complete
the actual Irp.
Arguments:
Dpc - not used.
DeferredContext - a pointer to the controller extension.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None
--*/
{
PCONTROLLER_EXTENSION controllerExtension = DeferredContext;
PDEVICE_OBJECT deviceObject = controllerExtension->DeviceObject;
PDEVICE_EXTENSION deviceExtension = deviceObject->DeviceExtension;
PIRP irp = controllerExtension->IrpAddress;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
BOOLEAN doingDma = controllerExtension->DataSelector ? FALSE : TRUE;
NTSTATUS status;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
//
// Cancel request timer.
//
KeCancelTimer(&controllerExtension->Timer);
//
// Check for resetting controller.
//
if (controllerExtension->PerformingReset == TRUE) {
controllerExtension->PerformingReset = FALSE;
//
// This is a reset/retry. Copy the saved request back into the
// request block and try again.
//
RtlMoveMemory(requestBlock,
controllerExtension->RetryRequestBlock,
REQUEST_BLOCK_LENGTH);
KeSetTimer(&controllerExtension->Timer,
controllerExtension->TimeoutValue,
&controllerExtension->TimerDpc);
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
deviceObject);
return;
}
//
// Check for failed request.
//
if (requestBlock->ReturnCode & ABIOS_RC_UNSUCCESSFUL) {
DebugPrint((1,
"AbiosDiskCompletionDpc: %s request failed - return code %lx\n"
"\trequest block %lx disk block %lx ttw %d\n",
(irpStack->MajorFunction == IRP_MJ_WRITE) ? "Write" : "Read",
requestBlock->ReturnCode,
requestBlock,
requestBlock->RelativeBlockAddress,
requestBlock->WaitTime));
//
// Check if transfer should be retried.
//
if ((requestBlock->ReturnCode & ABIOS_RC_RETRYABLE) &&
(controllerExtension->RetryCount)) {
controllerExtension->RetryCount--;
if (controllerExtension->RetryCount >= 1) {
//
// For all retries except for the last one (where RetryCount
// will be == 0), do not reset the controller.
//
RtlMoveMemory(requestBlock,
controllerExtension->RetryRequestBlock,
REQUEST_BLOCK_LENGTH);
KeSetTimer(&controllerExtension->Timer,
controllerExtension->TimeoutValue,
&controllerExtension->TimerDpc);
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
deviceObject);
return;
}
//
// Reset and retry this request.
//
DebugPrint((1,
"AbiosDiskCompletionDpc: Retries left %d request block %lx\n",
controllerExtension->RetryCount,
requestBlock));
//
// Reset the device.
//
RtlZeroMemory(requestBlock,
REQUEST_BLOCK_LENGTH);
requestBlock->Length = REQUEST_BLOCK_LENGTH;
requestBlock->Function = ABIOS_FUNCTION_RESET_DEVICE;
requestBlock->LogicalId = controllerExtension->LogicalId;
requestBlock->Unit = deviceExtension->Unit;
controllerExtension->PerformingReset = TRUE;
AbiosDiskLogError(deviceExtension,
requestBlock->ReturnCode,
STATUS_SUCCESS,
IO_ERR_NOT_READY);
//
// Start timing request.
//
KeSetTimer(&controllerExtension->Timer,
controllerExtension->TimeoutValue,
&controllerExtension->TimerDpc);
//
// Call synchronized routine to submit request block.
//
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
deviceObject);
return;
}
//
// Check return code of completing request block.
//
status = AbiosInterpretReturnCode(deviceExtension, requestBlock);
//
// Flush the DMA adapter channel.
//
if ((irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) && (doingDma)) {
IoFlushAdapterBuffers( controllerExtension->DmaAdapterObject,
irp->MdlAddress,
controllerExtension->MapRegisterBase,
controllerExtension->VirtualAddress,
0,
(BOOLEAN)
(irpStack->MajorFunction == IRP_MJ_READ ? FALSE : TRUE));
}
} else if (requestBlock->Function == ABIOS_FUNCTION_RESET_DEVICE) {
//
// Request timed out; Retries exhausted.
//
DebugPrint((1,
"AbiosDiskCompletionDpc: %s reset failed - return code %lx\n"
"\trequest block %lx disk block %lx ttw %d\n",
(irpStack->MajorFunction == IRP_MJ_WRITE) ? "Write" : "Read",
requestBlock->ReturnCode,
requestBlock,
requestBlock->RelativeBlockAddress,
requestBlock->WaitTime));
AbiosDiskLogError(deviceExtension,
2,
STATUS_IO_TIMEOUT,
IO_ERR_TIMEOUT);
status = STATUS_IO_TIMEOUT;
//
// Flush the DMA adapter channel.
//
if ((irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) && (doingDma)) {
IoFlushAdapterBuffers(controllerExtension->DmaAdapterObject,
irp->MdlAddress,
controllerExtension->MapRegisterBase,
controllerExtension->VirtualAddress,
0,
(BOOLEAN)
(irpStack->MajorFunction == IRP_MJ_READ ? FALSE : TRUE));
}
} else {
//
// This code will operate for both read and write as well as
// verify. When doing verify, bytesTransferred is really the
// count of bytes verified.
//
ULONG bytesTransferred = requestBlock->BlockCount * BYTES_PER_SECTOR;
//
// Check for successful retry.
//
if (controllerExtension->RetryCount != deviceExtension->SoftwareRetries) {
DebugPrint((0,
"AbiosDiskCompletionDpc: %s retry succeeded return code %lx\n"
"\trequest block %lx disk block %lx ttw %d\n",
(irpStack->MajorFunction == IRP_MJ_WRITE) ? "Write" : "Read",
requestBlock->ReturnCode,
requestBlock,
requestBlock->RelativeBlockAddress,
requestBlock->WaitTime));
AbiosDiskLogError(deviceExtension,
requestBlock->ReturnCode,
STATUS_SUCCESS,
IO_ERR_RETRY_SUCCEEDED);
}
//
// Flush the DMA adapter channel.
//
if ((irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) && (doingDma)) {
IoFlushAdapterBuffers(controllerExtension->DmaAdapterObject,
irp->MdlAddress,
controllerExtension->MapRegisterBase,
controllerExtension->VirtualAddress,
bytesTransferred,
(BOOLEAN)
(irpStack->MajorFunction == IRP_MJ_READ ? FALSE : TRUE));
}
//
// Increment bytes transferred.
//
controllerExtension->BytesTransferred += bytesTransferred;
controllerExtension->RemainingSectors -= requestBlock->BlockCount;
//
// Check if more sectors to transfer.
//
if (controllerExtension->RemainingSectors) {
DebugPrint((2,
"AbiosDiskCompletionDpc: " // no comma
"Blocks transferred %lx Sectors remaining %lx\n",
requestBlock->BlockCount,
controllerExtension->RemainingSectors));
//
// Calculate values for the next operation.
//
controllerExtension->StartingSector +=
controllerExtension->TransferSectors;
//
// The value of the virtual address may be zero if this is a
// verify request. Updating this value has no negative effects.
// so it is updated even in the verify case.
//
*((PULONG)&controllerExtension->VirtualAddress) += bytesTransferred;
if (!doingDma) {
*((PULONG)&controllerExtension->SystemAddress) += bytesTransferred;
}
controllerExtension->RetryCount = deviceExtension->SoftwareRetries;
//
// Build ABIOS request block and submit the request
//
AbiosBuildRequest(deviceExtension, irp);
KeSynchronizeExecution(controllerExtension->InterruptObject,
AbiosDiskSynchronizedIoStart,
deviceObject);
return;
}
status = STATUS_SUCCESS;
}
//
// Free the map registers.
//
if ((irpStack->MajorFunction != IRP_MJ_DEVICE_CONTROL) && (doingDma)) {
IoFreeMapRegisters(controllerExtension->DmaAdapterObject,
controllerExtension->MapRegisterBase,
controllerExtension->NumberOfMapRegisters);
}
//
// Update blocks transferred in IRP.
//
irp->IoStatus.Information += controllerExtension->BytesTransferred;
//
// Complete IRP.
//
irp->IoStatus.Status = status;
if (IoIsErrorUserInduced(irp->IoStatus.Status)) {
IoSetHardErrorOrVerifyDevice(irp, deviceObject);
}
IoCompleteRequest(irp, IO_DISK_INCREMENT);
//
// Set IRP pointer in controllerExtension to NULL and
// release controller.
//
controllerExtension->IrpAddress = NULL;
IoFreeController(controllerExtension->ControllerObject);
//
// Start next packet.
//
IoStartNextPacket(deviceObject, FALSE);
return;
} // end AbiosDiskCompletionDpc()
BOOLEAN
AbiosCheckReturnCode(
IN PVOID DeviceObject
)
/*++
Routine Description:
Check ABIOS request block completion code. Based on this code this routine
may stall execution or schedule a Dpc.
Arguments:
DeviceObject - pointer to the device object representing the device
returning the error code.
Return Value:
TRUE if interrupt serviced
(Not check if called from request submission routine)
--*/
{
PDEVICE_EXTENSION deviceExtension =
((PDEVICE_OBJECT) DeviceObject)->DeviceExtension;
PCONTROLLER_EXTENSION controllerExtension =
deviceExtension->ControllerExtension;
PABIOS_REQUEST_BLOCK requestBlock = controllerExtension->RequestBlock;
NTSTATUS status;
do {
//
// Check if request block is complete.
//
switch (requestBlock->ReturnCode) {
case ABIOS_RC_NOT_VALID:
//
// Request pending. Wait for interrupt.
//
return FALSE;
case ABIOS_RC_STAGE_INTERRUPT:
//
// Do nothing else for this interrupt.
//
return FALSE;
case ABIOS_RC_STAGE_TIME:
//
// Delay and call the abios interrupt routine again.
//
KeStallExecutionProcessor(requestBlock->WaitTime);
break;
case ABIOS_RC_NOT_MY_INTERRUPT:
DebugPrint((2,"AbiosCheckReturnCode: Spurious interrupt\n"));
//
// Treat this interrupt as spurious.
//
return FALSE;
default:
DebugPrint((3, "AbiosCheckReturnCode: Command complete\n"));
if (controllerExtension->IrpAddress) {
//
// Queue DPC to complete or restart this request block.
//
KeInsertQueueDpc(&controllerExtension->CompletionDpc,
controllerExtension->IrpAddress,
controllerExtension);
}
return TRUE;
} // end switch()
status = KeI386AbiosCall(controllerExtension->LogicalId,
controllerExtension->DriverObject,
controllerExtension->Selector,
ABIOS_INTERRUPT_ROUTINE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"AbiosCheckReturnCode: Abios call failed\n"));
return TRUE;
}
} while (TRUE);
} // end AbiosCheckReturnCode()
NTSTATUS
AbiosInterpretReturnCode(
IN PDEVICE_EXTENSION DeviceExtension,
IN PABIOS_REQUEST_BLOCK RequestBlock
)
/*++
Routine Description:
Map ABIOS return code to NT status. If an error occurred this routine
will make the appropriate mapping to an error log event and call the
routine to submit the error log entry. The NT status returned can
be used as the IoStatus returned to the upper layer drivers.
Arguments:
DeviceExtension - represents the device for the error.
RequestBlock - the ABIOS request block containing the error value.
Return Value:
NTSTATUS
--*/
{
USHORT returnClass;
USHORT returnCode;
NTSTATUS status;
NTSTATUS ioStatus;
returnCode = RequestBlock->ReturnCode;
returnClass = returnCode & ABIOS_RC_CLASS_MASK;
if (returnClass | ABIOS_RC_UNSUCCESSFUL) {
switch (returnClass) {
case ABIOS_RC_UNSUCCESSFUL:
if (returnCode == ABIOS_RC_DEVICE_IN_USE) {
ioStatus = IO_ERR_NOT_READY;
status = STATUS_DEVICE_BUSY;
} else {
ioStatus = IO_ERR_CONTROLLER_ERROR;
status = STATUS_UNSUCCESSFUL;
}
break;
default:
switch (returnCode & ABIOS_RC_CODE_MASK) {
case ABIOS_RC_RESET_FAILED:
ioStatus = IO_ERR_CONTROLLER_ERROR;
status = STATUS_DISK_RESET_FAILED;
break;
case ABIOS_RC_ADDRESS_MARK_NOT_FOUND:
ioStatus = IO_ERR_SEEK_ERROR;
status = STATUS_DATA_ERROR;
break;
case ABIOS_RC_BAD_SECTOR:
case ABIOS_RC_BAD_TRACK:
case ABIOS_RC_BAD_SECTOR_FORMAT:
case ABIOS_RC_WRITE_FAULT:
ioStatus = IO_ERR_BAD_BLOCK;
status = STATUS_DATA_ERROR;
break;
case ABIOS_RC_CRC_ERROR:
ioStatus = IO_ERR_BAD_BLOCK;
status = STATUS_CRC_ERROR;
break;
case ABIOS_RC_BAD_CONTROLLER:
ioStatus = IO_ERR_CONTROLLER_ERROR;
status = STATUS_ADAPTER_HARDWARE_ERROR;
break;
case ABIOS_RC_EQUIPMENT_CHECK:
case ABIOS_RC_DEVICE_DID_NOT_RESPOND:
ioStatus = IO_ERR_CONTROLLER_ERROR;
status = STATUS_IO_DEVICE_ERROR;
break;
case ABIOS_RC_DEVICE_IN_USE:
ioStatus = IO_ERR_NOT_READY;
status = STATUS_DEVICE_NOT_READY;
break;
default:
ioStatus = IO_ERR_CONTROLLER_ERROR;
status = STATUS_UNSUCCESSFUL;
break;
}
break;
}
} else {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
AbiosDiskLogError(DeviceExtension,
(ULONG) returnCode,
status,
ioStatus);
}
return status;
} // end AbiosInterpretReturnCode()
VOID
AbiosDiskTimeoutDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine is called when the ABIOS disk request timer expires.
The routine notifies ABIOS that a timeout has occurred.
Arguments:
Dpc - not used.
DeferredContext - address of controller extension
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None
--*/
{
PCONTROLLER_EXTENSION controllerExtension = DeferredContext;
PIRP irp = controllerExtension->IrpAddress;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
DebugPrint((0,
"AbiosDiskTimeoutDpc: Request (%x) to %x timed out\n",
irp,
controllerExtension));
ASSERT(controllerExtension->PerformingReset == FALSE);
//
// Notifiy ABIOS that request timed out.
//
status = KeI386AbiosCall(controllerExtension->LogicalId,
controllerExtension->DriverObject,
controllerExtension->Selector,
ABIOS_TIMEOUT_ROUTINE);
//
// Let normal return code processing handle the error condition.
//
AbiosCheckReturnCode(controllerExtension->DeviceObject);
return;
} // end AbiosDiskTimeoutDpc()
NTSTATUS
AbiosDiskDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O subsystem to perform device specific
control functions. Most of these functions are satisfied immediately
within this routine. Others (such as VERIFY) will require additional
processing.
Arguments:
DeviceObject - the object for the device control.
Irp - the device control request.
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
ULONG sectorOffset;
PVERIFY_INFORMATION verifyInfo;
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
DebugPrint((3,"AbiosDeviceIoControl: Get drive geometry\n"));
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
&deviceExtension->DiskGeometry,
sizeof(DISK_GEOMETRY));
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
break;
case IOCTL_DISK_CHECK_VERIFY:
//
// This driver does not handle device that
// support removable media.
//
DebugPrint((3, "AbiosDeviceIoControl: Check verify\n"));
break;
case IOCTL_DISK_VERIFY:
//
// Set up the Irp for processing.
//
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
//
// Doctor up the Irp to put the verify information into the
// stack parameter block.
//
verifyInfo = Irp->AssociatedIrp.SystemBuffer;
irpStack->Parameters.Read.Length = verifyInfo->Length;
irpStack->Parameters.Read.ByteOffset.QuadPart =
verifyInfo->StartingOffset.QuadPart + deviceExtension->StartingOffset.QuadPart;
//
// Calculate the key and queue or start IRP.
//
sectorOffset = (ULONG) (irpStack->Parameters.Read.ByteOffset.QuadPart >>
deviceExtension->SectorShift);
IoStartPacket(deviceExtension->PhysicalDevice->DeviceObject,
Irp,
&sectorOffset,
NULL);
return STATUS_PENDING;
break;
case IOCTL_DISK_GET_PARTITION_INFO:
//
// Return the information about the partition specified by the
// device object. Note that no information is ever returned about
// the size or partition type of the physical disk.
//
DebugPrint((3,"AbiosDeviceIoControl: Get partition info\n"));
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(PARTITION_INFORMATION)) {
DebugPrint((3, "AbiosDeviceControl: Buffer length %lx\n",
irpStack->Parameters.DeviceIoControl.OutputBufferLength));
status = STATUS_INVALID_PARAMETER;
} else if ((deviceExtension->PartitionType == 0) ||
(deviceExtension->PartitionNumber == 0)) {
DebugPrint((3, "AbiosDeviceControl: PartitionType %d\n",
deviceExtension->PartitionType));
status = STATUS_INVALID_DEVICE_REQUEST;
} else {
PPARTITION_INFORMATION outputBuffer;
outputBuffer =
(PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
outputBuffer->PartitionType = deviceExtension->PartitionType;
outputBuffer->StartingOffset = deviceExtension->StartingOffset;
outputBuffer->PartitionLength= deviceExtension->PartitionLength;
outputBuffer->HiddenSectors = deviceExtension->HiddenSectors;
outputBuffer->PartitionNumber = deviceExtension->PartitionNumber;
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
}
break;
case IOCTL_DISK_SET_PARTITION_INFO:
DebugPrint((3,"AbiosDeviceIoControl: Set partition info\n"));
if (deviceExtension->PartitionNumber == 0) {
status = STATUS_UNSUCCESSFUL;
} else {
PSET_PARTITION_INFORMATION inputBuffer =
(PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
status = IoSetPartitionInformation(
deviceExtension->PhysicalDevice->DeviceObject,
deviceExtension->DiskGeometry.BytesPerSector,
(ULONG) deviceExtension->PartitionNumber,
inputBuffer->PartitionType);
if (NT_SUCCESS(status)) {
deviceExtension->PartitionType = inputBuffer->PartitionType;
}
}
break;
case IOCTL_DISK_GET_DRIVE_LAYOUT:
//
// Return the partition layout for the physical drive. Note that
// the layout is returned for the actual physical drive, regardless
// of which partition was specified for the request.
//
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DRIVE_LAYOUT_INFORMATION ) ) {
status = STATUS_INVALID_PARAMETER;
} else {
PDRIVE_LAYOUT_INFORMATION partitionList;
status = IoReadPartitionTable(deviceExtension->DeviceObject,
deviceExtension->DiskGeometry.BytesPerSector,
FALSE,
&partitionList);
if (NT_SUCCESS(status)) {
ULONG tempSize;
//
// The disk layout has been returned in the partitionList
// buffer. Determine its size and, if the data will fit
// into the intermediary buffer, return it.
//
tempSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,
PartitionEntry[0]);
tempSize += partitionList->PartitionCount *
sizeof(PARTITION_INFORMATION);
if (tempSize >
irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
} else {
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
partitionList,
tempSize );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = tempSize;
UpdateDeviceObjects(DeviceObject,
Irp);
}
//
// Free the buffer allocated by reading the partition
// table and update the partition numbers for the user.
//
ExFreePool(partitionList);
}
}
break;
case IOCTL_DISK_SET_DRIVE_LAYOUT:
{
//
// Update the disk with new partition information.
//
PDRIVE_LAYOUT_INFORMATION partitionList =
Irp->AssociatedIrp.SystemBuffer;
//
// Call routine to create, delete and change device objects to
// reflect new disk layout.
//
UpdateDeviceObjects(DeviceObject,
Irp);
//
// Write new layout to disk.
//
status = IoWritePartitionTable(
deviceExtension->DeviceObject,
deviceExtension->DiskGeometry.BytesPerSector,
deviceExtension->DiskGeometry.SectorsPerTrack,
deviceExtension->DiskGeometry.TracksPerCylinder,
partitionList);
if (NT_SUCCESS(status)) {
Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
}
break;
}
case IOCTL_DISK_INTERNAL_SET_VERIFY:
//
// If the caller is kernel mode, set the verify bit.
//
if (Irp->RequestorMode == KernelMode) {
DeviceObject->Flags |= DO_VERIFY_VOLUME;
}
break;
case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
//
// If the caller is kernel mode, clear the verify bit.
//
if (Irp->RequestorMode == KernelMode) {
DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
}
break;
default:
DebugPrint((3,
"AbiosIoDeviceControl: Unsupported device IOCTL\n"));
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
} // end AbiosDiskDeviceControl()
#if DBG
VOID
AbiosDebugPrint(
ULONG DebugPrintLevel,
PCCHAR DebugMessage,
...
)
/*++
Routine Description:
Debug print for ABIOS 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 <= AbiosDebug) {
char buffer[128];
vsprintf(buffer, DebugMessage, ap);
DbgPrint(buffer);
}
va_end(ap);
} // end DebugPrint(
#endif
VOID
AbiosDiskLogError(
IN PDEVICE_EXTENSION DeviceExtension,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIoStatus
)
/*++
Routine Description:
This routine performs error logging for the Abios disk driver.
This consists of allocating an error log packet and if this is
successful, filling in the details provided as parameters.
Arguments:
DeviceExtension - Extension representing failing device.
UniqueErrorValue - Values defined to uniquely identify error location.
FinalStatus - Status returned for failure.
SpecificIoStatus - IO error status value.
Irp - If there is an irp this is the pointer to it.
Return Value:
None
--*/
{
PIO_ERROR_LOG_PACKET errorLogPacket;
errorLogPacket = IoAllocateErrorLogEntry(DeviceExtension->DeviceObject,
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET)));
if (errorLogPacket != NULL) {
errorLogPacket->SequenceNumber = AbiosDiskErrorLogSequence++;
errorLogPacket->ErrorCode = SpecificIoStatus;
errorLogPacket->FinalStatus = FinalStatus;
errorLogPacket->UniqueErrorValue = UniqueErrorValue;
errorLogPacket->DumpDataSize = 0;
IoWriteErrorLogEntry(errorLogPacket);
}
} // end AbiosDiskLogError()
VOID
UpdateDeviceObjects(
IN PDEVICE_OBJECT PhysicalDisk,
IN PIRP Irp
)
/*++
Routine Description:
This routine creates, deletes and changes device objects when
the IOCTL_SET_DRIVE_LAYOUT is called. This routine also updates the
partition numbers in the drive layout structure. It is possible to
call this routine even on a GET_LAYOUT case because RewritePartition
is set to FALSE.
Arguments:
DeviceObject - Device object for physical disk.
Irp - IO Request Packet (IRP).
Return Value:
None.
--*/
{
PDEVICE_EXTENSION physicalExtension = PhysicalDisk->DeviceExtension;
PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
ULONG partition;
ULONG partitionNumber;
ULONG partitionCount;
ULONG lastPartition;
PPARTITION_INFORMATION partitionEntry;
CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
STRING ntNameString;
UNICODE_STRING ntUnicodeString;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION deviceExtension;
PDEVICE_EXTENSION lastExtension;
NTSTATUS status;
BOOLEAN found;
partitionCount = ((partitionList->PartitionCount + 3) / 4) * 4;
//
// LastExtension is used to link new partitions onto the partition
// chain anchored at the physical extension. Preset this variable
// to the physical extension in case no partitions exist on this disk
// before this set drive layout.
//
lastExtension = physicalExtension;
//
// Zero all of the partition numbers.
//
for (partition = 0; partition < partitionCount; partition++) {
partitionEntry = &partitionList->PartitionEntry[partition];
partitionEntry->PartitionNumber = 0;
}
//
// Walk through chain of partitions for this disk to determine
// which existing partitions have no match.
//
deviceExtension = physicalExtension;
lastPartition = 0;
do {
deviceExtension = deviceExtension->NextPartition;
//
// Check if this is the last partition in the chain.
//
if (!deviceExtension) {
break;
}
//
// Check for highest partition number this far.
//
if (deviceExtension->PartitionNumber > lastPartition) {
lastPartition = deviceExtension->PartitionNumber;
}
//
// Check if this partition is not currently being used.
//
if (!deviceExtension->PartitionLength.QuadPart) {
continue;
}
//
// Loop through partition information to look for match.
//
found = FALSE;
for (partition = 0;
partition < partitionCount;
partition++) {
//
// Get partition descriptor.
//
partitionEntry = &partitionList->PartitionEntry[partition];
//
// Check if empty, or describes extended partiton or hasn't changed.
//
if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
IsContainerPartition(partitionEntry->PartitionType)) {
continue;
}
//
// Check if new partition starts where this partition starts.
//
if (partitionEntry->StartingOffset.QuadPart != deviceExtension->StartingOffset.QuadPart) {
continue;
}
//
// Check if partition length is the same.
//
if (partitionEntry->PartitionLength.QuadPart == deviceExtension->PartitionLength.QuadPart) {
DebugPrint((1,
"UpdateDeviceObjects: Found match for \\Harddisk%d\\Partition%d\n",
physicalExtension->DiskNumber,
deviceExtension->PartitionNumber));
//
// Indicate match is found and set partition number
// in user buffer.
//
found = TRUE;
partitionEntry->PartitionNumber = deviceExtension->PartitionNumber;
break;
}
}
if (found) {
//
// A match is found. If this partition is marked for update,
// check for a partition type change.
//
if (partitionEntry->RewritePartition) {
deviceExtension->PartitionType = partitionEntry->PartitionType;
}
} else {
//
// no match was found, indicate this partition is gone.
//
DebugPrint((1,
"UpdateDeviceObjects: Deleting \\Device\\Harddisk%x\\Partition%x\n",
physicalExtension->DiskNumber,
deviceExtension->PartitionNumber));
deviceExtension->PartitionLength.QuadPart = 0;
}
} while (TRUE);
//
// Walk through partition loop to find new partitions and set up
// device extensions to describe them. In some cases new device
// objects will be created.
//
for (partition = 0;
partition < partitionCount;
partition++) {
//
// Get partition descriptor.
//
partitionEntry = &partitionList->PartitionEntry[partition];
//
// Check if empty, or describes extended partiton or hasn't changed.
//
if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
IsContainerPartition(partitionEntry->PartitionType) ||
!partitionEntry->RewritePartition) {
continue;
}
if (partitionEntry->PartitionNumber) {
//
// Partition is being rewritten, but already exists as a device object
//
continue;
}
//
// Check first if existing device object is available by
// walking partition extension list.
//
partitionNumber = 0;
deviceExtension = physicalExtension;
do {
//
// Get next partition device extension from disk data.
//
deviceExtension = deviceExtension->NextPartition;
if (!deviceExtension) {
break;
}
//
// A device object is free if the partition length is set to zero.
//
if (!deviceExtension->PartitionLength.QuadPart) {
partitionNumber = deviceExtension->PartitionNumber;
break;
}
lastExtension = deviceExtension;
} while (TRUE);
//
// If partition number is still zero then a new device object
// must be created.
//
if (partitionNumber == 0) {
lastPartition++;
partitionNumber = lastPartition;
//
// Get or create partition object and set up partition parameters.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d\\Partition%d",
physicalExtension->DiskNumber,
partitionNumber);
RtlInitString(&ntNameString,
ntNameBuffer);
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
DebugPrint((1,
"UpdateDeviceObjects: Create device object %s\n",
ntNameBuffer));
//
// This is a new name. Create the device object to represent it.
//
status = IoCreateDevice(PhysicalDisk->DriverObject,
DEVICE_EXTENSION_SIZE,
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&deviceObject);
RtlFreeUnicodeString(&ntUnicodeString);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"UpdateDeviceObjects: Can't create device %s\n",
ntNameBuffer));
continue;
}
//
// Set up device object fields.
//
deviceObject->Flags |= DO_DIRECT_IO;
deviceObject->StackSize = PhysicalDisk->StackSize;
//
// Set up device extension fields.
//
deviceExtension = deviceObject->DeviceExtension;
//
// Copy physical disk extension to partition extension.
//
RtlMoveMemory(deviceExtension,
physicalExtension,
sizeof(DEVICE_EXTENSION));
//
// Clear flags initializing bit.
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Point back at device object.
//
deviceExtension->DeviceObject = deviceObject;
//
// Link to end of partition chain using previous disk data.
//
lastExtension->NextPartition = deviceExtension;
deviceExtension->NextPartition = NULL;
} else {
DebugPrint((1,
"UpdateDeviceObjects: Used existing device object \\Device\\Harddisk%x\\Partition%x\n",
physicalExtension->DiskNumber,
partitionNumber));
}
//
// Update partition information in partition device extension.
//
deviceExtension->PartitionNumber = (USHORT)partitionNumber;
deviceExtension->PartitionType = partitionEntry->PartitionType;
deviceExtension->BootIndicator = partitionEntry->BootIndicator;
deviceExtension->StartingOffset = partitionEntry->StartingOffset;
deviceExtension->PartitionLength = partitionEntry->PartitionLength;
deviceExtension->HiddenSectors = partitionEntry->HiddenSectors;
//
// Update partition number passed in to indicate the
// device name for this partition.
//
partitionEntry->PartitionNumber = partitionNumber;
}
} // end UpdateDeviceObjects()
#else // defined(i386)
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the main entry point for this driver. This routine exists so
that this driver can be built for non-x86 platforms and still be loaded
as a driver into the system, since it must be loaded by the OS loader
during the initial boot phase. It simply returns a status that indicates
that it did not successfully initialize, and will therefore allows the
system to boot.
Arguments:
DriverObject - Supplies a pointer to the driver object that represents
the loaded instantiation of this driver in memory.
Return Value:
The final return status is always an error.
--*/
{
//
// Simply return an error and get out of here.
//
return STATUS_DEVICE_DOES_NOT_EXIST;
}
#endif // defined(i386)