summaryrefslogtreecommitdiff
path: root/storage/class/disk/src
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-04-29 09:48:49 -0700
committerDave Wilson <[email protected]>2015-04-29 10:47:29 -0700
commitd40232638faaab19de557e70a3ee938da7a35295 (patch)
tree14e61c973745949af186b7940e3a53d25322f5de /storage/class/disk/src
parent02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff)
samples update for //build
Diffstat (limited to 'storage/class/disk/src')
-rw-r--r--storage/class/disk/src/data.c85
-rw-r--r--storage/class/disk/src/disk.c6166
-rw-r--r--storage/class/disk/src/disk.h1257
-rw-r--r--storage/class/disk/src/disk.htm42
-rw-r--r--storage/class/disk/src/disk.rc23
-rw-r--r--storage/class/disk/src/disk.vcxproj195
-rw-r--r--storage/class/disk/src/disk.vcxproj.Filters43
-rw-r--r--storage/class/disk/src/diskdev.inf113
-rw-r--r--storage/class/disk/src/diskwmi.c3501
-rw-r--r--storage/class/disk/src/enum.c1313
-rw-r--r--storage/class/disk/src/geometry.c1672
-rw-r--r--storage/class/disk/src/pnp.c1098
12 files changed, 15508 insertions, 0 deletions
diff --git a/storage/class/disk/src/data.c b/storage/class/disk/src/data.c
new file mode 100644
index 00000000..52f273ab
--- /dev/null
+++ b/storage/class/disk/src/data.c
@@ -0,0 +1,85 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "disk.h"
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg("PAGEDATA")
+#endif
+
+/*
+#define HackDisableTaggedQueuing (0x01)
+#define HackDisableSynchronousTransfers (0x02)
+#define HackDisableSpinDown (0x04)
+#define HackDisableWriteCache (0x08)
+#define HackCauseNotReportableHack (0x10)
+#define HackRequiresStartUnitCommand (0x20)
+*/
+
+CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[] = {
+ { "COMPAQ" , "PD-1" , NULL, 0x02 },
+ { "CONNER" , "CP3500" , NULL, 0x02 },
+ { "FUJITSU" , "M2652S-512" , NULL, 0x01 },
+ { "HP ", "C1113F " , NULL, 0x20 },
+ // iomegas require START_UNIT commands so be sure to match all of them.
+ { "iomega" , "jaz" , NULL, 0x30 },
+ { "iomega" , NULL , NULL, 0x20 },
+ { "IOMEGA" , "ZIP" , NULL, 0x27 },
+ { "IOMEGA" , NULL , NULL, 0x20 },
+ { "MAXTOR" , "MXT-540SL" , "I1.2", 0x01 },
+ { "MICROP" , "1936-21MW1002002" , NULL, 0x03 },
+ { "OLIVETTI", "CP3500" , NULL, 0x02 },
+ { "SEAGATE" , "ST41601N" , "0102", 0x02 },
+ { "SEAGATE" , "ST3655N" , NULL, 0x08 },
+ { "SEAGATE" , "ST3390N" , NULL, 0x08 },
+ { "SEAGATE" , "ST12550N" , NULL, 0x08 },
+ { "SEAGATE" , "ST32430N" , NULL, 0x08 },
+ { "SEAGATE" , "ST31230N" , NULL, 0x08 },
+ { "SEAGATE" , "ST15230N" , NULL, 0x08 },
+ { "SyQuest" , "SQ5110" , "CHC", 0x03 },
+ { "TOSHIBA" , "MK538FB" , "60", 0x01 },
+ { NULL , NULL , NULL, 0x0 }
+};
+
+DISK_MEDIA_TYPES_LIST const DiskMediaTypesExclude[] = {
+ { "HP" , "RDX" , NULL, 0, 0, 0 , 0 , 0 , 0 },
+ { NULL , NULL , NULL, 0, 0, 0 , 0 , 0 , 0 }
+};
+
+
+DISK_MEDIA_TYPES_LIST const DiskMediaTypes[] = {
+ { "COMPAQ" , "PD-1 LF-1094" , NULL, 1, 1, PC_5_RW , 0 , 0 , 0 },
+ { "HP" , NULL , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { "iomega" , "jaz" , NULL, 1, 1, IOMEGA_JAZ , 0 , 0 , 0 },
+ { "IOMEGA" , "ZIP" , NULL, 1, 1, IOMEGA_ZIP , 0 , 0 , 0 },
+ { "PINNACLE", "Apex 4.6GB" , NULL, 3, 2, PINNACLE_APEX_5_RW, MO_5_RW, MO_5_WO, 0 },
+ { "SONY" , "SMO-F541" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { "SONY" , "SMO-F551" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { "SONY" , "SMO-F561" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { "Maxoptix", "T5-2600" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { "Maxoptix", "T6-5200" , NULL, 2, 2, MO_5_WO , MO_5_RW, 0 , 0 },
+ { NULL , NULL , NULL, 0, 0, 0 , 0 , 0 , 0 }
+};
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg()
+#endif
diff --git a/storage/class/disk/src/disk.c b/storage/class/disk/src/disk.c
new file mode 100644
index 00000000..3d4d19b4
--- /dev/null
+++ b/storage/class/disk/src/disk.c
@@ -0,0 +1,6166 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#define DEBUG_MAIN_SOURCE 1
+#include "disk.h"
+
+
+//
+// Now instantiate the GUIDs
+//
+
+#include "initguid.h"
+#include "ntddstor.h"
+#include "ntddvol.h"
+#include "ioevent.h"
+
+#ifdef DEBUG_USE_WPP
+#include "disk.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, DiskUnload)
+#pragma alloc_text(PAGE, DiskCreateFdo)
+#pragma alloc_text(PAGE, DiskDetermineMediaTypes)
+#pragma alloc_text(PAGE, DiskModeSelect)
+#pragma alloc_text(PAGE, DisableWriteCache)
+#pragma alloc_text(PAGE, DiskSetSpecialHacks)
+#pragma alloc_text(PAGE, DiskGetCacheInformation)
+#pragma alloc_text(PAGE, DiskSetCacheInformation)
+#pragma alloc_text(PAGE, DiskLogCacheInformation)
+#pragma alloc_text(PAGE, DiskSetInfoExceptionInformation)
+#pragma alloc_text(PAGE, DiskGetInfoExceptionInformation)
+#pragma alloc_text(PAGE, DiskIoctlGetCacheSetting)
+#pragma alloc_text(PAGE, DiskIoctlSetCacheSetting)
+#pragma alloc_text(PAGE, DiskIoctlGetLengthInfo)
+#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometry)
+#pragma alloc_text(PAGE, DiskIoctlGetDriveGeometryEx)
+#pragma alloc_text(PAGE, DiskIoctlGetCacheInformation)
+#pragma alloc_text(PAGE, DiskIoctlSetCacheInformation)
+#pragma alloc_text(PAGE, DiskIoctlGetMediaTypesEx)
+#pragma alloc_text(PAGE, DiskIoctlPredictFailure)
+#pragma alloc_text(PAGE, DiskIoctlEnableFailurePrediction)
+#pragma alloc_text(PAGE, DiskIoctlReassignBlocks)
+#pragma alloc_text(PAGE, DiskIoctlReassignBlocksEx)
+#pragma alloc_text(PAGE, DiskIoctlIsWritable)
+#pragma alloc_text(PAGE, DiskIoctlUpdateDriveSize)
+#pragma alloc_text(PAGE, DiskIoctlGetVolumeDiskExtents)
+#pragma alloc_text(PAGE, DiskIoctlSmartGetVersion)
+#pragma alloc_text(PAGE, DiskIoctlSmartReceiveDriveData)
+#pragma alloc_text(PAGE, DiskIoctlSmartSendDriveCommand)
+#pragma alloc_text(PAGE, DiskIoctlVerifyThread)
+
+#endif
+
+//
+// ETW related globals
+//
+BOOLEAN DiskETWEnabled = FALSE;
+
+BOOLEAN DiskIsPastReinit = FALSE;
+
+const GUID GUID_NULL = { 0 };
+#define DiskCompareGuid(_First,_Second) \
+ (memcmp ((_First),(_Second), sizeof (GUID)))
+
+//
+// This macro is used to work around a bug in the definition of
+// DISK_CACHE_RETENTION_PRIORITY. The value KeepReadData should be
+// assigned 0xf rather than 0x2. Since the interface was already published
+// when this was discovered the disk driver has been modified to translate
+// between the interface value and the correct scsi value.
+//
+// 0x2 is turned into 0xf
+// 0xf is turned into 0x2 - this ensures that future SCSI defintions can be
+// accomodated.
+//
+
+#define TRANSLATE_RETENTION_PRIORITY(_x)\
+ ((_x) == 0xf ? 0x2 : \
+ ((_x) == 0x2 ? 0xf : _x) \
+ )
+
+#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN CTL_CODE(IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+VOID
+DiskDriverReinit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ )
+{
+ UNREFERENCED_PARAMETER(DriverObject);
+ UNREFERENCED_PARAMETER(Nothing);
+ UNREFERENCED_PARAMETER(Count);
+
+ DiskIsPastReinit = TRUE;
+}
+
+VOID
+DiskBootDriverReinit(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ )
+{
+ IoRegisterDriverReinitialization(DriverObject, DiskDriverReinit, NULL);
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ DiskDriverReinitialization(DriverObject, Nothing, Count);
+
+#else
+
+ UNREFERENCED_PARAMETER(Nothing);
+ UNREFERENCED_PARAMETER(Count);
+
+#endif
+
+}
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the SCSI hard disk class driver.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ RegistryPath - Pointer to the name of the services node for this driver.
+
+Return Value:
+
+ The function value is the final status from the initialization operation.
+
+--*/
+
+{
+ CLASS_INIT_DATA InitializationData = { 0 };
+ CLASS_QUERY_WMI_REGINFO_EX_LIST classQueryWmiRegInfoExList = { 0 };
+ GUID guidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
+ GUID guidSrbSupport = GUID_CLASSPNP_SRB_SUPPORT;
+ ULONG srbSupport;
+
+ NTSTATUS status;
+
+ //
+ // Initializes tracing
+ //
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ //
+ // Read the information NtDetect squirreled away about the disks in this
+ // system.
+ //
+
+ DiskSaveDetectInfo(DriverObject);
+
+#endif
+
+ InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
+
+ //
+ // Setup sizes and entry points for functional device objects
+ //
+
+ InitializationData.FdoData.DeviceExtensionSize = FUNCTIONAL_EXTENSION_SIZE;
+ InitializationData.FdoData.DeviceType = FILE_DEVICE_DISK;
+ InitializationData.FdoData.DeviceCharacteristics = FILE_DEVICE_SECURE_OPEN;
+
+ InitializationData.FdoData.ClassInitDevice = DiskInitFdo;
+ InitializationData.FdoData.ClassStartDevice = DiskStartFdo;
+ InitializationData.FdoData.ClassStopDevice = DiskStopDevice;
+ InitializationData.FdoData.ClassRemoveDevice = DiskRemoveDevice;
+ InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler;
+
+ InitializationData.FdoData.ClassError = DiskFdoProcessError;
+ InitializationData.FdoData.ClassReadWriteVerification = DiskReadWriteVerification;
+ InitializationData.FdoData.ClassDeviceControl = DiskDeviceControl;
+ InitializationData.FdoData.ClassShutdownFlush = DiskShutdownFlush;
+ InitializationData.FdoData.ClassCreateClose = NULL;
+
+
+ InitializationData.FdoData.ClassWmiInfo.GuidCount = 7;
+ InitializationData.FdoData.ClassWmiInfo.GuidRegInfo = DiskWmiFdoGuidList;
+ InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiRegInfo = DiskFdoQueryWmiRegInfo;
+ InitializationData.FdoData.ClassWmiInfo.ClassQueryWmiDataBlock = DiskFdoQueryWmiDataBlock;
+ InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataBlock = DiskFdoSetWmiDataBlock;
+ InitializationData.FdoData.ClassWmiInfo.ClassSetWmiDataItem = DiskFdoSetWmiDataItem;
+ InitializationData.FdoData.ClassWmiInfo.ClassExecuteWmiMethod = DiskFdoExecuteWmiMethod;
+ InitializationData.FdoData.ClassWmiInfo.ClassWmiFunctionControl = DiskWmiFunctionControl;
+
+ InitializationData.ClassAddDevice = DiskAddDevice;
+ InitializationData.ClassUnload = DiskUnload;
+
+ //
+ // Initialize regregistration data structures
+ //
+
+ DiskInitializeReregistration();
+
+ //
+ // Call the class init routine
+ //
+
+ status = ClassInitialize(DriverObject, RegistryPath, &InitializationData);
+
+ if (NT_SUCCESS(status)) {
+
+ IoRegisterBootDriverReinitialization(DriverObject,
+ DiskBootDriverReinit,
+ NULL);
+ }
+
+ //
+ // Call class init Ex routine to register a
+ // PCLASS_QUERY_WMI_REGINFO_EX routine
+ //
+
+ classQueryWmiRegInfoExList.Size = sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST);
+ classQueryWmiRegInfoExList.ClassFdoQueryWmiRegInfoEx = DiskFdoQueryWmiRegInfoEx;
+
+ (VOID)ClassInitializeEx(DriverObject,
+ &guidQueryRegInfoEx,
+ &classQueryWmiRegInfoExList);
+
+ //
+ // Call class init Ex routine to register SRB support
+ //
+ srbSupport = CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK;
+ if (!NT_SUCCESS(ClassInitializeEx(DriverObject,
+ &guidSrbSupport,
+ &srbSupport))) {
+ //
+ // Should not fail
+ //
+ NT_ASSERT(FALSE);
+ }
+
+
+ return status;
+
+} // end DriverEntry()
+
+
+VOID
+DiskUnload(
+ IN PDRIVER_OBJECT DriverObject
+ )
+{
+ PAGED_CODE();
+
+#if defined(_X86_) || defined(_AMD64_)
+ DiskCleanupDetectInfo(DriverObject);
+#else
+ // NB: Need to use UNREFERENCED_PARAMETER to prevent build error
+ // DriverObject is not referenced below in WPP_CLEANUP.
+ // WPP_CLEANUP is currently an "NOOP" marco.
+ UNREFERENCED_PARAMETER(DriverObject);
+#endif
+
+
+ //
+ // Cleans up tracing
+ //
+ WPP_CLEANUP(DriverObject);
+
+ return;
+}
+
+
+NTSTATUS
+DiskCreateFdo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT PhysicalDeviceObject,
+ IN PULONG DeviceCount,
+ IN BOOLEAN DasdAccessOnly
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates an object for the functional device
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ PhysicalDeviceObject - Lower level driver we should attach to
+
+ DeviceCount - Number of previously installed devices.
+
+ DasdAccessOnly - indicates whether or not a file system is allowed to mount
+ on this device object. Used to avoid double-mounting of
+ file systems on super-floppies (which can unfortunately be
+ fixed disks). If set the i/o system will only allow rawfs
+ to be mounted.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+ PCCHAR deviceName = NULL;
+ HANDLE handle = NULL;
+ PDEVICE_OBJECT lowerDevice = NULL;
+ PDEVICE_OBJECT deviceObject = NULL;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ *DeviceCount = 0;
+
+ //
+ // Set up an object directory to contain the objects for this
+ // device and all its partitions.
+ //
+
+ do {
+
+ WCHAR dirBuffer[64] = { 0 };
+ UNICODE_STRING dirName;
+ OBJECT_ATTRIBUTES objectAttribs;
+
+ status = RtlStringCchPrintfW(dirBuffer, sizeof(dirBuffer) / sizeof(dirBuffer[0]) - 1, L"\\Device\\Harddisk%d", *DeviceCount);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Format symbolic link failed with error: 0x%X\n", status));
+ return status;
+ }
+
+ RtlInitUnicodeString(&dirName, dirBuffer);
+
+ InitializeObjectAttributes(&objectAttribs,
+ &dirName,
+ OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwCreateDirectoryObject(&handle,
+ DIRECTORY_ALL_ACCESS,
+ &objectAttribs);
+
+ (*DeviceCount)++;
+
+ } while((status == STATUS_OBJECT_NAME_COLLISION) ||
+ (status == STATUS_OBJECT_NAME_EXISTS));
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Could not create directory - %lx\n", status));
+
+ return(status);
+ }
+
+ //
+ // When this loop exits the count is inflated by one - fix that.
+ //
+
+ (*DeviceCount)--;
+
+ //
+ // Claim the device.
+ //
+
+ lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
+
+ status = ClassClaimDevice(lowerDevice, FALSE);
+
+ if (!NT_SUCCESS(status)) {
+ ZwMakeTemporaryObject(handle);
+ ZwClose(handle);
+ ObDereferenceObject(lowerDevice);
+ return status;
+ }
+
+ //
+ // Create a device object for this device. Each physical disk 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.
+ //
+
+ status = DiskGenerateDeviceName(*DeviceCount, &deviceName);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo - couldn't create name %lx\n", status));
+
+ goto DiskCreateFdoExit;
+
+ }
+
+ status = ClassCreateDeviceObject(DriverObject,
+ deviceName,
+ PhysicalDeviceObject,
+ TRUE,
+ &deviceObject);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, "DiskCreateFdo: Can not create device object %s\n", deviceName));
+ goto DiskCreateFdoExit;
+ }
+
+ FREE_POOL(deviceName);
+
+ //
+ // Indicate that IRPs should include MDLs for data transfers.
+ //
+
+ SET_FLAG(deviceObject->Flags, DO_DIRECT_IO);
+
+ fdoExtension = deviceObject->DeviceExtension;
+
+ if(DasdAccessOnly) {
+
+ //
+ // Inidicate that only RAW should be allowed to mount on the root
+ // partition object. This ensures that a file system can't doubly
+ // mount on a super-floppy by mounting once on P0 and once on P1.
+ //
+
+#pragma prefast(suppress:28175);
+ SET_FLAG(deviceObject->Vpb->Flags, VPB_RAW_MOUNT);
+ }
+
+ //
+ // Initialize lock count to zero. The lock count is used to
+ // disable the ejection mechanism on devices that support
+ // removable media. Only the lock count in the physical
+ // device extension is used.
+ //
+
+ fdoExtension->LockCount = 0;
+
+ //
+ // Save system disk number.
+ //
+
+ fdoExtension->DeviceNumber = *DeviceCount;
+
+ //
+ // Set the alignment requirements for the device based on the
+ // host adapter requirements
+ //
+
+ if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
+ deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
+ }
+
+ //
+ // Finally, attach to the pdo
+ //
+
+ fdoExtension->LowerPdo = PhysicalDeviceObject;
+
+ fdoExtension->CommonExtension.LowerDeviceObject =
+ IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
+
+
+ if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
+
+ //
+ // Uh - oh, we couldn't attach
+ // cleanup and return
+ //
+
+ status = STATUS_UNSUCCESSFUL;
+ goto DiskCreateFdoExit;
+ }
+
+ //
+ // Clear the init flag.
+ //
+
+ CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);
+
+ //
+ // Store a handle to the device object directory for this disk
+ //
+
+ fdoExtension->DeviceDirectory = handle;
+
+ ObDereferenceObject(lowerDevice);
+
+ return STATUS_SUCCESS;
+
+DiskCreateFdoExit:
+
+ if (deviceObject != NULL)
+ {
+ IoDeleteDevice(deviceObject);
+ }
+
+ FREE_POOL(deviceName);
+
+ ObDereferenceObject(lowerDevice);
+
+ ZwMakeTemporaryObject(handle);
+ ZwClose(handle);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadWriteVerification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ I/O System entry for read and write requests to SCSI disks.
+
+Arguments:
+
+ DeviceObject - Pointer to driver object created by system.
+ Irp - IRP involved.
+
+Return Value:
+
+ NT Status
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG residualBytes;
+ ULONG residualOffset;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // Make sure that the request is within the bounds of the partition,
+ // the number of bytes to transfer and the byte offset are a
+ // multiple of the sector size.
+ //
+
+ residualBytes = irpSp->Parameters.Read.Length & (commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);
+ residualOffset = irpSp->Parameters.Read.ByteOffset.LowPart & (commonExtension->PartitionZeroExtension->DiskGeometry.BytesPerSector - 1);
+
+ if ((irpSp->Parameters.Read.ByteOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
+ (irpSp->Parameters.Read.ByteOffset.QuadPart < 0) ||
+ (residualBytes != 0) ||
+ (residualOffset != 0))
+ {
+ NT_ASSERT(residualOffset == 0);
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - irpSp->Parameters.Read.ByteOffset.QuadPart;
+
+ if ((ULONGLONG)irpSp->Parameters.Read.Length > bytesRemaining)
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ //
+ // This error may be caused by the fact that the drive is not ready.
+ //
+
+ status = ((PDISK_DATA) commonExtension->DriverData)->ReadyStatus;
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // Flag this as a user error so that a popup is generated.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "DiskReadWriteVerification: ReadyStatus is %lx\n", status));
+
+ if (IoIsErrorUserInduced(status) && Irp->Tail.Overlay.Thread != NULL) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+
+ //
+ // status will keep the current error
+ //
+
+ } else if ((residualBytes == 0) && (residualOffset == 0)) {
+
+ //
+ // This failed because we think the physical disk is too small.
+ // Send it down to the drive and let the hardware decide for
+ // itself.
+ //
+
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Note fastfat depends on this parameter to determine when to
+ // remount due to a sector size change.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+
+ return status;
+
+} // end DiskReadWrite()
+
+
+NTSTATUS
+DiskDetermineMediaTypes(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN UCHAR MediumType,
+ IN UCHAR DensityCode,
+ IN BOOLEAN MediaPresent,
+ IN BOOLEAN IsWritable
+ )
+
+/*++
+
+Routine Description:
+
+ Determines number of types based on the physical device, validates the user buffer
+ and builds the MEDIA_TYPE information.
+
+Arguments:
+
+ DeviceObject - Pointer to functional device object created by system.
+ Irp - IOCTL_STORAGE_GET_MEDIA_TYPES_EX Irp.
+ MediumType - byte returned in mode data header.
+ DensityCode - byte returned in mode data block descriptor.
+ NumberOfTypes - pointer to be updated based on actual device.
+
+Return Value:
+
+ Status is returned.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer;
+ PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0];
+ BOOLEAN deviceMatched = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // this should be checked prior to calling into this routine
+ // as we use the buffer as mediaTypes
+ //
+
+ NT_ASSERT(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(GET_MEDIA_TYPES));
+
+ //
+ // Determine if this device is removable or fixed.
+ //
+
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Fixed disk.
+ //
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = 1;
+
+ mediaInfo->DeviceSpecific.DiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.DiskInfo.MediaType = FixedMedia;
+ mediaInfo->DeviceSpecific.DiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.DiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.DiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.DiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics = (MEDIA_CURRENTLY_MOUNTED | MEDIA_READ_WRITE);
+
+ if (!IsWritable) {
+
+ SET_FLAG(mediaInfo->DeviceSpecific.DiskInfo.MediaCharacteristics,
+ MEDIA_WRITE_PROTECTED);
+ }
+
+ } else {
+
+ PCCHAR vendorId = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset;
+ PCCHAR productId = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductIdOffset;
+ PCCHAR productRevision = (PCCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductRevisionOffset;
+ DISK_MEDIA_TYPES_LIST const *mediaListEntry;
+ ULONG currentMedia;
+ ULONG i;
+ ULONG j;
+ ULONG sizeNeeded;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "DiskDetermineMediaTypes: Vendor %s, Product %s\n",
+ vendorId,
+ productId));
+
+
+ //
+ // If there's an entry with such vendorId & ProductId in the DiskMediaTypesExclude list,
+ // this device shouldn't be looked up in the DiskMediaTypes list to determine a medium type.
+ // The exclude table allows to narrow down the set of devices described by the DiskMediaTypes
+ // list (e.g.: DiskMediaTypes says "all HP devices" and DiskMediaTypesExlclude says
+ // "except for HP RDX")
+ //
+
+ for (i = 0; DiskMediaTypesExclude[i].VendorId != NULL; i++) {
+ mediaListEntry = &DiskMediaTypesExclude[i];
+
+ if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->ProductId != NULL) &&
+ strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) {
+ continue;
+ }
+
+ goto SkipTable;
+ }
+
+ //
+ // Run through the list until we find the entry with a NULL Vendor Id.
+ //
+
+ for (i = 0; DiskMediaTypes[i].VendorId != NULL; i++) {
+
+ mediaListEntry = &DiskMediaTypes[i];
+
+ if (strncmp(mediaListEntry->VendorId,vendorId,strlen(mediaListEntry->VendorId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->ProductId != NULL) &&
+ strncmp(mediaListEntry->ProductId, productId, strlen(mediaListEntry->ProductId))) {
+ continue;
+ }
+
+ if ((mediaListEntry->Revision != NULL) &&
+ strncmp(mediaListEntry->Revision, productRevision, strlen(mediaListEntry->Revision))) {
+ continue;
+ }
+
+ deviceMatched = TRUE;
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = mediaListEntry->NumberOfTypes;
+
+ //
+ // Ensure that buffer is large enough.
+ //
+
+ sizeNeeded = FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
+ (mediaListEntry->NumberOfTypes *
+ sizeof(DEVICE_MEDIA_INFO)
+ );
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeNeeded) {
+
+ //
+ // Buffer too small
+ //
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ for (j = 0; j < mediaListEntry->NumberOfTypes; j++) {
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = mediaListEntry->NumberOfSides;
+
+ //
+ // Set the type.
+ //
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = mediaListEntry->MediaTypes[j];
+
+ if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == MO_5_WO) {
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_WRITE_ONCE;
+ } else {
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
+ }
+
+ //
+ // Status will either be success, if media is present, or no media.
+ // It would be optimal to base from density code and medium type, but not all devices
+ // have values for these fields.
+ //
+
+ if (MediaPresent) {
+
+ //
+ // The usage of MediumType and DensityCode is device specific, so this may need
+ // to be extended to further key off of product/vendor ids.
+ // Currently, the MO units are the only devices that return this information.
+ //
+
+ if (MediumType == 2) {
+ currentMedia = MO_5_WO;
+ } else if (MediumType == 3) {
+ currentMedia = MO_5_RW;
+
+ if (DensityCode == 0x87) {
+
+ //
+ // Indicate that the pinnacle 4.6 G media
+ // is present. Other density codes will default to normal
+ // RW MO media.
+ //
+
+ currentMedia = PINNACLE_APEX_5_RW;
+ }
+ } else {
+ currentMedia = 0;
+ }
+
+ if (currentMedia) {
+ if (mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType == (STORAGE_MEDIA_TYPE)currentMedia) {
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
+ }
+
+ } else {
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
+ }
+ }
+
+ if (!IsWritable) {
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
+ }
+
+ //
+ // Advance to next entry.
+ //
+
+ mediaInfo++;
+ }
+ }
+
+SkipTable:
+
+ if (!deviceMatched) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
+ "DiskDetermineMediaTypes: Unknown device. Vendor: %s Product: %s Revision: %s\n",
+ vendorId,
+ productId,
+ productRevision));
+ //
+ // Build an entry for unknown.
+ //
+
+ mediaTypes->DeviceType = FILE_DEVICE_DISK;
+ mediaTypes->MediaInfoCount = 1;
+
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
+
+ if (MediaPresent) {
+
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
+ }
+
+ if (!IsWritable) {
+
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_WRITE_PROTECTED);
+ }
+ }
+ }
+
+ Irp->IoStatus.Information =
+ FIELD_OFFSET(GET_MEDIA_TYPES, MediaInfo[0]) +
+ (mediaTypes->MediaInfoCount * sizeof(DEVICE_MEDIA_INFO));
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DiskDeviceControl(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ I/O system entry for device controls to SCSI disks.
+
+Arguments:
+
+ Fdo - Pointer to functional device object created by system.
+ Irp - IRP involved.
+
+Return Value:
+
+ Status is returned.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG ioctlCode;
+
+ NT_ASSERT(DeviceObject != NULL);
+
+ Irp->IoStatus.Information = 0;
+ ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DiskDeviceControl: Received IOCTL 0x%X for device %p through IRP %p\n",
+ ioctlCode, DeviceObject, Irp));
+
+
+ switch (ioctlCode) {
+
+ case IOCTL_DISK_GET_CACHE_INFORMATION: {
+ status = DiskIoctlGetCacheInformation(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_SET_CACHE_INFORMATION: {
+ status = DiskIoctlSetCacheInformation(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_CACHE_SETTING: {
+ status = DiskIoctlGetCacheSetting(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_SET_CACHE_SETTING: {
+ status = DiskIoctlSetCacheSetting(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
+ status = DiskIoctlGetDriveGeometry(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: {
+ status = DiskIoctlGetDriveGeometryEx( DeviceObject, Irp );
+ break;
+ }
+
+ case IOCTL_DISK_VERIFY: {
+ status = DiskIoctlVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_GET_LENGTH_INFO: {
+ status = DiskIoctlGetLengthInfo(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_IS_WRITABLE: {
+ status = DiskIoctlIsWritable(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_UPDATE_DRIVE_SIZE: {
+ status = DiskIoctlUpdateDriveSize(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_REASSIGN_BLOCKS: {
+ status = DiskIoctlReassignBlocks(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_REASSIGN_BLOCKS_EX: {
+ status = DiskIoctlReassignBlocksEx(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_INTERNAL_SET_VERIFY: {
+ status = DiskIoctlSetVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_DISK_INTERNAL_CLEAR_VERIFY: {
+ status = DiskIoctlClearVerify(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
+ status = DiskIoctlGetMediaTypesEx(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_PREDICT_FAILURE : {
+ status = DiskIoctlPredictFailure(DeviceObject, Irp);
+ break;
+ }
+
+ #if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ case IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG : {
+ status = DiskIoctlEnableFailurePrediction(DeviceObject, Irp);
+ break;
+ }
+ #endif
+
+ case SMART_GET_VERSION: {
+ status = DiskIoctlSmartGetVersion(DeviceObject, Irp);
+ break;
+ }
+
+ case SMART_RCV_DRIVE_DATA: {
+ status = DiskIoctlSmartReceiveDriveData(DeviceObject, Irp);
+ break;
+ }
+
+ case SMART_SEND_DRIVE_COMMAND: {
+ status = DiskIoctlSmartSendDriveCommand(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
+ case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN: {
+ status = DiskIoctlGetVolumeDiskExtents(DeviceObject, Irp);
+ break;
+ }
+
+ default: {
+
+ //
+ // Pass the request to the common device control routine.
+ //
+ return(ClassDeviceControl(DeviceObject, Irp));
+ break;
+ }
+ } // end switch
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskDeviceControl: IOCTL 0x%X to device %p failed with error 0x%X\n",
+ ioctlCode, DeviceObject, status));
+ if (IoIsErrorUserInduced(status) &&
+ (Irp->Tail.Overlay.Thread != NULL)) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+ }
+
+ //
+ // DiskIoctlVerify() (IOCTL_DISK_VERIFY) function returns STATUS_PENDING
+ // and completes the IRP in the work item. Do not touch or complete
+ // the IRP if STATUS_PENDING is returned.
+ //
+
+ if (status != STATUS_PENDING) {
+
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ return(status);
+} // end DiskDeviceControl()
+
+NTSTATUS
+DiskShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the handler for shutdown and flush requests. It sends
+ down a synch cache command to the device if its cache is enabled. If
+ the request is a shutdown and the media is removable, it sends down
+ an unlock request
+
+ Finally, an SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH is sent down
+ the stack
+
+Arguments:
+
+ DeviceObject - The device object processing the request
+ Irp - The shutdown | flush request being serviced
+
+Return Value:
+
+ STATUS_PENDING if successful, an error code otherwise
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
+ PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData;
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG srbSize;
+ PSCSI_REQUEST_BLOCK srb;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+ PCDB cdb;
+ KIRQL irql;
+
+ //
+ // Flush requests are combined and need to be handled in a special manner
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) {
+
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED)) {
+
+ //
+ // We've been assured that both the disk
+ // and adapter caches are battery-backed
+ //
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_SUCCESS;
+ }
+
+ KeAcquireSpinLock(&diskData->FlushContext.Spinlock, &irql);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: IRP %p flags = 0x%x\n", Irp, irpStack->Flags));
+
+ //
+ // This request will most likely be completed asynchronously
+ //
+ IoMarkIrpPending(Irp);
+
+ //
+ // Look to see if a flush is in progress
+ //
+
+ if (diskData->FlushContext.CurrIrp != NULL) {
+
+ //
+ // There is an outstanding flush. Queue this
+ // request to the group that is next in line
+ //
+
+ if (diskData->FlushContext.NextIrp != NULL) {
+
+ #if DBG
+ diskData->FlushContext.DbgTagCount++;
+ #endif
+
+ InsertTailList(&diskData->FlushContext.NextList, &Irp->Tail.Overlay.ListEntry);
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ //
+ // This request will be completed by its representative
+ //
+
+ } else {
+
+ #if DBG
+ if (diskData->FlushContext.DbgTagCount < 64) {
+
+ diskData->FlushContext.DbgRefCount[diskData->FlushContext.DbgTagCount]++;
+ }
+
+ diskData->FlushContext.DbgSavCount += diskData->FlushContext.DbgTagCount;
+ diskData->FlushContext.DbgTagCount = 0;
+ #endif
+
+ diskData->FlushContext.NextIrp = Irp;
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
+
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: waiting for event\n"));
+
+ //
+ // Wait for the outstanding flush to complete
+ //
+ KeWaitForSingleObject(&diskData->FlushContext.Event, Executive, KernelMode, FALSE, NULL);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskShutdownFlush: event signal\n"));
+
+ //
+ // Make this group the outstanding one and free up the next slot
+ //
+
+ KeAcquireSpinLock(&diskData->FlushContext.Spinlock, &irql);
+
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
+
+ while (!IsListEmpty(&diskData->FlushContext.NextList)) {
+
+ PLIST_ENTRY listEntry = RemoveHeadList(&diskData->FlushContext.NextList);
+ InsertTailList(&diskData->FlushContext.CurrList, listEntry);
+ }
+
+ NT_ASSERT(diskData->FlushContext.CurrIrp != diskData->FlushContext.NextIrp);
+ diskData->FlushContext.CurrIrp = diskData->FlushContext.NextIrp;
+ diskData->FlushContext.NextIrp = NULL;
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ //
+ // Send this request down to the device
+ //
+ DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
+ }
+
+ } else {
+
+ diskData->FlushContext.CurrIrp = Irp;
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.CurrList));
+
+ NT_ASSERT(diskData->FlushContext.NextIrp == NULL);
+ NT_ASSERT(IsListEmpty(&diskData->FlushContext.NextList));
+
+
+ KeReleaseSpinLock(&diskData->FlushContext.Spinlock, irql);
+
+ DiskFlushDispatch(DeviceObject, &diskData->FlushContext);
+ }
+
+ } else {
+
+ //
+ // Allocate SCSI request block.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+ if (srb == NULL) {
+
+ //
+ // Set the status and complete the request.
+ //
+
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ RtlZeroMemory(srb, srbSize);
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value and mark the request as not being a tagged request.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue * 4;
+ srbEx->RequestTag = SP_UNTAGGED;
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->SrbFlags = fdoExtension->SrbFlags;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ //
+ // Set the status and complete the request.
+ //
+
+ Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(STATUS_INTERNAL_ERROR);
+ }
+
+ } else {
+
+ //
+ // Write length to SRB.
+ //
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+
+ //
+ // Set timeout value and mark the request as not being a tagged request.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue * 4;
+ srb->QueueTag = SP_UNTAGGED;
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->SrbFlags = fdoExtension->SrbFlags;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ cdb = (PCDB)srb->Cdb;
+ }
+
+ //
+ // If the write cache is enabled then send a synchronize cache request.
+ //
+
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE)) {
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbExDataCdb16->CdbLength = 10;
+ } else {
+ srb->CdbLength = 10;
+ }
+
+ cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ TRUE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskShutdownFlush: Synchonize cache sent. Status = %lx\n", status));
+ }
+
+ //
+ // Unlock the device if it contains removable media
+ //
+
+ if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
+ {
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reinitialize status fields to 0 in case there was a previous request
+ //
+
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+
+ srbExDataCdb16->CdbLength = 6;
+
+ //
+ // Set timeout value
+ //
+
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ } else {
+
+ //
+ // Reinitialize status fields to 0 in case there was a previous request
+ //
+
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+ }
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+ cdb->MEDIA_REMOVAL.Prevent = FALSE;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ TRUE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskShutdownFlush: Unlock device request sent. Status = %lx\n", status));
+ }
+
+ //
+ // Set up a SHUTDOWN SRB
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->NumSrbExData = 0;
+ srbEx->SrbExDataOffset[0] = 0;
+ srbEx->SrbFunction = SRB_FUNCTION_SHUTDOWN;
+ srbEx->OriginalRequest = Irp;
+ srbEx->SrbLength = CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE;
+ srbEx->SrbStatus = 0;
+ } else {
+ srb->CdbLength = 0;
+ srb->Function = SRB_FUNCTION_SHUTDOWN;
+ srb->SrbStatus = 0;
+ srb->OriginalRequest = Irp;
+ }
+
+ //
+ // Set the retry count to zero.
+ //
+
+ irpStack->Parameters.Others.Argument4 = (PVOID) 0;
+
+ //
+ // Set up IoCompletion routine address.
+ //
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, srb, TRUE, TRUE, TRUE);
+
+ //
+ // Get next stack location and
+ // set major function code.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(Irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Set up SRB for execute scsi request.
+ // Save SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // Call the port driver to process the request.
+ //
+
+ IoMarkIrpPending(Irp);
+ IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+
+ return STATUS_PENDING;
+}
+
+
+VOID
+DiskFlushDispatch(
+ IN PDEVICE_OBJECT Fdo,
+ IN PDISK_GROUP_CONTEXT FlushContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the handler for flush requests. It sends down a synch
+ cache command to the device if its cache is enabled. This is followed
+ by an SRB_FUNCTION_FLUSH
+
+Arguments:
+
+ Fdo - The device object processing the flush request
+ FlushContext - The flush group context
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb = &FlushContext->Srb.Srb;
+ PSTORAGE_REQUEST_BLOCK srbEx = &FlushContext->Srb.SrbEx;
+ PIO_STACK_LOCATION irpSp = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+
+ //
+ // Fill in the srb fields appropriately
+ //
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ RtlZeroMemory(srbEx, sizeof(FlushContext->Srb.SrbExBuffer));
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(FlushContext->Srb.SrbExBuffer);
+ srbEx->RequestPriority = IoGetIoPriorityHint(FlushContext->CurrIrp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->TimeOutValue = fdoExt->TimeOutValue * 4;
+ srbEx->RequestTag = SP_UNTAGGED;
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->SrbFlags = fdoExt->SrbFlags;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ } else {
+ RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->TimeOutValue = fdoExt->TimeOutValue * 4;
+ srb->QueueTag = SP_UNTAGGED;
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->SrbFlags = fdoExt->SrbFlags;
+ }
+
+ //
+ // If write caching is enabled then send down a synchronize cache request
+ //
+ if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE))
+ {
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->NumSrbExData = 1;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 10;
+ srbExDataCdb16->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ } else {
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 10;
+ srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch: sending sync cache\n"));
+
+ ClassSendSrbSynchronous(Fdo, srb, NULL, 0, TRUE);
+ }
+
+ //
+ // Set up a FLUSH SRB
+ //
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->SrbFunction = SRB_FUNCTION_FLUSH;
+ srbEx->NumSrbExData = 0;
+ srbEx->SrbExDataOffset[0] = 0;
+ srbEx->OriginalRequest = FlushContext->CurrIrp;
+ srbEx->SrbStatus = 0;
+
+ //
+ // Make sure that this srb does not get freed
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
+
+ } else {
+ srb->Function = SRB_FUNCTION_FLUSH;
+ srb->CdbLength = 0;
+ srb->OriginalRequest = FlushContext->CurrIrp;
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+
+ //
+ // Make sure that this srb does not get freed
+ //
+ SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT);
+ }
+
+ //
+ // Make sure that this request does not get retried
+ //
+ irpSp = IoGetCurrentIrpStackLocation(FlushContext->CurrIrp);
+
+ irpSp->Parameters.Others.Argument4 = (PVOID) 0;
+
+ //
+ // Fill in the irp fields appropriately
+ //
+ irpSp = IoGetNextIrpStackLocation(FlushContext->CurrIrp);
+
+ irpSp->MajorFunction = IRP_MJ_SCSI;
+ irpSp->Parameters.Scsi.Srb = srb;
+
+ IoSetCompletionRoutine(FlushContext->CurrIrp, DiskFlushComplete, NULL, TRUE, TRUE, TRUE);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushDispatch: sending srb flush on irp %p\n", FlushContext->CurrIrp));
+
+ //
+ // Send down the flush request
+ //
+ IoCallDriver(((PCOMMON_DEVICE_EXTENSION)fdoExt)->LowerDeviceObject, FlushContext->CurrIrp);
+}
+
+
+
+NTSTATUS
+DiskFlushComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This completion routine is a wrapper around ClassIoComplete. It
+ will complete all the flush requests that are tagged to it, set
+ an event to signal the next group to proceed and return
+
+Arguments:
+
+ Fdo - The device object which requested the completion routine
+ Irp - The irp that is being completed
+ Context - The flush group context
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PDISK_GROUP_CONTEXT FlushContext;
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PDISK_DATA diskData;
+
+ UNREFERENCED_PARAMETER(Context);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "DiskFlushComplete: %p %p\n", Fdo, Irp));
+
+ //
+ // Get the flush context from the device extension
+ //
+ fdoExt = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ diskData = (PDISK_DATA)fdoExt->CommonExtension.DriverData;
+ NT_ASSERT(diskData != NULL);
+ _Analysis_assume_(diskData != NULL);
+
+ FlushContext = &diskData->FlushContext;
+
+ //
+ // Make sure everything is in order
+ //
+ NT_ASSERT(Irp == FlushContext->CurrIrp);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: completing irp %p\n", Irp));
+ status = ClassIoComplete(Fdo, Irp, &FlushContext->Srb.Srb);
+
+ //
+ // Make sure that ClassIoComplete did not decide to retry this request
+ //
+ NT_ASSERT(status != STATUS_MORE_PROCESSING_REQUIRED);
+
+ //
+ // Complete the flush requests tagged to this one
+ //
+
+ while (!IsListEmpty(&FlushContext->CurrList)) {
+
+ PLIST_ENTRY listEntry = RemoveHeadList(&FlushContext->CurrList);
+ PIRP tempIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+
+ InitializeListHead(&tempIrp->Tail.Overlay.ListEntry);
+ tempIrp->IoStatus = Irp->IoStatus;
+
+ ClassReleaseRemoveLock(Fdo, tempIrp);
+ ClassCompleteRequest(Fdo, tempIrp, IO_NO_INCREMENT);
+ }
+
+
+ //
+ // Notify the next group's representative that it may go ahead now
+ //
+ KeSetEvent(&FlushContext->Event, IO_NO_INCREMENT, FALSE);
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_SCSI, "DiskFlushComplete: irp %p status = 0x%x\n", Irp, status));
+
+ return status;
+}
+
+
+
+NTSTATUS
+DiskModeSelect(
+ IN PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ IN ULONG Length,
+ IN BOOLEAN SavePage
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sends a mode select command.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ ModeSelectBuffer - Supplies a buffer containing the page data.
+
+ Length - Supplies the length in bytes of the mode select buffer.
+
+ SavePage - Indicates that parameters should be written to disk.
+
+Return Value:
+
+ Length of the transferred data is returned.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb;
+ SCSI_REQUEST_BLOCK srb = {0};
+ ULONG retries = 1;
+ ULONG length2;
+ NTSTATUS status;
+ PULONG buffer;
+ PMODE_PARAMETER_BLOCK blockDescriptor;
+ UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+ PSCSI_REQUEST_BLOCK srbPtr;
+
+ PAGED_CODE();
+
+ //
+ // Check whether block length is available
+ //
+
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect: Block length is not available. Unable to send mode select\n"));
+ NT_ASSERT(fdoExtension->DiskGeometry.BytesPerSector != 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+
+
+ length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
+
+ //
+ // Allocate buffer for mode select header, block descriptor, and mode page.
+ //
+
+ buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ length2,
+ DISK_TAG_MODE_DATA);
+
+ if (buffer == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(buffer, length2);
+
+ //
+ // Set length in header to size of mode page.
+ //
+
+ ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
+
+ blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);
+
+ //
+ // Set block length from the cached disk geometry
+ //
+
+ blockDescriptor->BlockLength[2] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector >> 16);
+ blockDescriptor->BlockLength[1] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector >> 8);
+ blockDescriptor->BlockLength[0] = (UCHAR) (fdoExtension->DiskGeometry.BytesPerSector);
+
+ //
+ // Copy mode page to buffer.
+ //
+
+ RtlCopyMemory(buffer + 3, ModeSelectBuffer, Length);
+
+ //
+ // Build the MODE SELECT CDB.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(srbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoPriorityNormal;
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value from device extension.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue * 2;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(buffer);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskModeSelect: Insufficient extended SRB size\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
+
+ } else {
+
+ srb.CdbLength = 6;
+ cdb = (PCDB)srb.Cdb;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srb.TimeOutValue = fdoExtension->TimeOutValue * 2;
+
+ srbPtr = &srb;
+ }
+
+ cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+ cdb->MODE_SELECT.SPBit = SavePage;
+ cdb->MODE_SELECT.PFBit = 1;
+ cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);
+
+Retry:
+
+ status = ClassSendSrbSynchronous(Fdo,
+ srbPtr,
+ buffer,
+ length2,
+ TRUE);
+
+ if (status == STATUS_VERIFY_REQUIRED) {
+
+ //
+ // Routine ClassSendSrbSynchronous does not retry requests returned with
+ // this status.
+ //
+
+ if (retries--) {
+
+ //
+ // Retry request.
+ //
+
+ goto Retry;
+ }
+
+ } else if (SRB_STATUS(srbPtr->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ FREE_POOL(buffer);
+
+ return status;
+} // end DiskModeSelect()
+
+
+//
+// This routine is structured as a work-item routine
+//
+VOID
+DisableWriteCache(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ DISK_CACHE_INFORMATION cacheInfo = { 0 };
+ NTSTATUS status;
+ PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
+
+ PAGED_CODE();
+
+ NT_ASSERT(WorkItem != NULL);
+ _Analysis_assume_(WorkItem != NULL);
+
+ status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
+
+ if (NT_SUCCESS(status) && (cacheInfo.WriteCacheEnabled == TRUE)) {
+
+ cacheInfo.WriteCacheEnabled = FALSE;
+
+ DiskSetCacheInformation(fdoExtension, &cacheInfo);
+ }
+
+ IoFreeWorkItem(WorkItem);
+}
+
+
+//
+// This routine is structured as a work-item routine
+//
+VOID
+DiskIoctlVerifyThread(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+{
+ PDISK_VERIFY_WORKITEM_CONTEXT WorkContext = (PDISK_VERIFY_WORKITEM_CONTEXT)Context;
+ PIRP Irp = NULL;
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
+ PDISK_DATA DiskData = (PDISK_DATA)FdoExtension->CommonExtension.DriverData;
+ PVERIFY_INFORMATION verifyInfo = NULL;
+ PSCSI_REQUEST_BLOCK Srb = NULL;
+ PCDB Cdb = NULL;
+ LARGE_INTEGER byteOffset;
+ LARGE_INTEGER sectorOffset;
+ ULONG sectorCount;
+ NTSTATUS status = STATUS_SUCCESS;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ PAGED_CODE();
+
+ NT_ASSERT(WorkContext != NULL);
+ _Analysis_assume_(WorkContext != NULL);
+
+ Srb = WorkContext->Srb;
+ Irp = WorkContext->Irp;
+ verifyInfo = (PVERIFY_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // We don't need to hold on to this memory as
+ // the following operation may take some time
+ //
+
+ IoFreeWorkItem(WorkContext->WorkItem);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerifyThread: Spliting up the request\n"));
+
+ //
+ // Add disk offset to starting the sector
+ //
+
+ byteOffset.QuadPart = FdoExtension->CommonExtension.StartingOffset.QuadPart +
+ verifyInfo->StartingOffset.QuadPart;
+
+ //
+ // Convert byte offset to the sector offset
+ //
+
+ sectorOffset.QuadPart = byteOffset.QuadPart >> FdoExtension->SectorShift;
+
+ //
+ // Convert byte count to sector count.
+ //
+
+ sectorCount = verifyInfo->Length >> FdoExtension->SectorShift;
+
+ //
+ // Make sure that all previous verify requests have indeed completed
+ // This greatly reduces the possibility of a Denial-of-Service attack
+ //
+
+ KeWaitForMutexObject(&DiskData->VerifyMutex,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ //
+ // Initialize SCSI SRB for a verify CDB
+ //
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ RtlZeroMemory(Srb, CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE);
+ srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+
+ Cdb = (PCDB)srbExDataCdb16->Cdb;
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ srbExDataCdb16->CdbLength = 16;
+ Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
+ } else {
+ srbExDataCdb16->CdbLength = 10;
+ Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
+ }
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(Srb);
+ FREE_POOL(WorkContext);
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ RtlZeroMemory(Srb, SCSI_REQUEST_BLOCK_SIZE);
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ Cdb = (PCDB)Srb->Cdb;
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ Srb->CdbLength = 16;
+ Cdb->CDB16.OperationCode = SCSIOP_VERIFY16;
+ } else {
+ Srb->CdbLength = 10;
+ Cdb->CDB10.OperationCode = SCSIOP_VERIFY;
+ }
+
+ }
+
+ while (NT_SUCCESS(status) && (sectorCount != 0)) {
+
+ USHORT numSectors = (USHORT) min(sectorCount, MAX_SECTORS_PER_VERIFY);
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reset status fields
+ //
+
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+
+ //
+ // Calculate the request timeout value based
+ // on the number of sectors being verified
+ //
+
+ srbEx->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue;
+ } else {
+
+ //
+ // Reset status fields
+ //
+
+ Srb->SrbStatus = 0;
+ Srb->ScsiStatus = 0;
+
+ //
+ // Calculate the request timeout value based
+ // on the number of sectors being verified
+ //
+
+ Srb->TimeOutValue = ((numSectors + 0x7F) >> 7) * FdoExtension->TimeOutValue;
+ }
+
+ //
+ // Update verify CDB info.
+ // NOTE - CDB opcode and length has been initialized prior to entering
+ // the while loop
+ //
+
+ if (TEST_FLAG(FdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+
+ REVERSE_BYTES_QUAD(&Cdb->CDB16.LogicalBlock, &sectorOffset);
+ REVERSE_BYTES_SHORT(&Cdb->CDB16.TransferLength[2], &numSectors);
+ } else {
+
+ //
+ // Move little endian values into CDB in big endian format
+ //
+
+ Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
+ Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
+ Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
+ Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
+
+ Cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numSectors)->Byte1;
+ Cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numSectors)->Byte0;
+ }
+
+ status = ClassSendSrbSynchronous(Fdo,
+ Srb,
+ NULL,
+ 0,
+ FALSE);
+
+ NT_ASSERT(status != STATUS_NONEXISTENT_SECTOR);
+
+ sectorCount -= numSectors;
+ sectorOffset.QuadPart += numSectors;
+ }
+
+ KeReleaseMutex(&DiskData->VerifyMutex, FALSE);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ ClassReleaseRemoveLock(Fdo, Irp);
+ ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
+
+ FREE_POOL(Srb);
+ FREE_POOL(WorkContext);
+}
+
+
+VOID
+DiskFdoProcessError(
+ PDEVICE_OBJECT Fdo,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine checks the type of error. If the error indicates an underrun
+ then indicate the request should be retried.
+
+Arguments:
+
+ Fdo - Supplies a pointer to the functional device object.
+
+ Srb - Supplies a pointer to the failing Srb.
+
+ Status - Status with which the IRP will be completed.
+
+ Retry - Indication of whether the request will be retried.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PCDB cdb = NULL;
+ UCHAR scsiStatus = 0;
+ UCHAR senseBufferLength = 0;
+ PVOID senseBuffer = NULL;
+ CDB noOp = {0};
+
+ //
+ // Get relevant fields from SRB
+ //
+ if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)Srb;
+
+ //
+ // Look for SCSI SRB specific fields
+ //
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ cdb = GetSrbScsiData(srbEx, NULL, NULL, &scsiStatus, &senseBuffer, &senseBufferLength);
+
+ //
+ // cdb and sense buffer should not be NULL
+ //
+ NT_ASSERT(cdb != NULL);
+ NT_ASSERT(senseBuffer != NULL);
+
+ }
+
+ if (cdb == NULL) {
+
+ //
+ // Use a cdb that is all 0s
+ //
+ cdb = &noOp;
+ }
+
+ } else {
+
+ cdb = (PCDB)(Srb->Cdb);
+ scsiStatus = Srb->ScsiStatus;
+ senseBufferLength = Srb->SenseInfoBufferLength;
+ senseBuffer = Srb->SenseInfoBuffer;
+ }
+
+ if (*Status == STATUS_DATA_OVERRUN &&
+ (cdb != NULL) &&
+ (IS_SCSIOP_READWRITE(cdb->CDB10.OperationCode))) {
+
+ *Retry = TRUE;
+
+ //
+ // Update the error count for the device.
+ //
+
+ fdoExtension->ErrorCount++;
+
+ } else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
+ scsiStatus == SCSISTAT_BUSY) {
+
+ //
+ // a disk drive should never be busy this long. Reset the scsi bus
+ // maybe this will clear the condition.
+ //
+
+ ResetBus(Fdo);
+
+ //
+ // Update the error count for the device.
+ //
+
+ fdoExtension->ErrorCount++;
+
+ } else {
+
+ BOOLEAN invalidatePartitionTable = FALSE;
+
+ //
+ // See if this might indicate that something on the drive has changed.
+ //
+
+ if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
+ (senseBuffer != NULL) && (cdb != NULL)) {
+
+ BOOLEAN validSense = FALSE;
+ UCHAR senseKey = 0;
+ UCHAR asc = 0;
+ UCHAR ascq = 0;
+
+ validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
+ senseBufferLength,
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &asc,
+ &ascq);
+
+ if (validSense) {
+
+ switch (senseKey) {
+
+ case SCSI_SENSE_ILLEGAL_REQUEST: {
+
+ switch (asc) {
+
+ case SCSI_ADSENSE_INVALID_CDB:
+ {
+ //
+ // Look to see if this is an Io request with the ForceUnitAccess flag set
+ //
+ if (((cdb->CDB10.OperationCode == SCSIOP_WRITE) ||
+ (cdb->CDB10.OperationCode == SCSIOP_WRITE16)) &&
+ (cdb->CDB10.ForceUnitAccess))
+ {
+ PDISK_DATA diskData = (PDISK_DATA)fdoExtension->CommonExtension.DriverData;
+
+ if (diskData->WriteCacheOverride == DiskWriteCacheEnable)
+ {
+ PIO_ERROR_LOG_PACKET logEntry = NULL;
+
+ //
+ // The user has explicitly requested that write caching be turned on.
+ // Warn the user that writes with FUA enabled are not working and that
+ // they should disable write cache.
+ //
+
+ logEntry = IoAllocateErrorLogEntry(fdoExtension->DeviceObject,
+ sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+ logEntry->FinalStatus = *Status;
+ logEntry->ErrorCode = IO_WARNING_WRITE_FUA_PROBLEM;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode = IRP_MJ_SCSI;
+ logEntry->IoControlCode = 0;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = 0;
+ logEntry->DumpDataSize = 4 * sizeof(ULONG);
+
+ logEntry->DumpData[0] = diskData->ScsiAddress.PortNumber;
+ logEntry->DumpData[1] = diskData->ScsiAddress.PathId;
+ logEntry->DumpData[2] = diskData->ScsiAddress.TargetId;
+ logEntry->DumpData[3] = diskData->ScsiAddress.Lun;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+ }
+ else
+ {
+ //
+ // Turn off write caching on this device. This is so that future
+ // critical requests need not be sent down with ForceUnitAccess
+ //
+ PIO_WORKITEM workItem = IoAllocateWorkItem(Fdo);
+
+ if (workItem)
+ {
+ IoQueueWorkItem(workItem, DisableWriteCache, CriticalWorkQueue, workItem);
+ }
+ }
+
+ SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED);
+ ADJUST_FUA_FLAG(fdoExtension);
+
+
+ cdb->CDB10.ForceUnitAccess = FALSE;
+ *Retry = TRUE;
+
+ } else if ((cdb->CDB6FORMAT.OperationCode == SCSIOP_MODE_SENSE) &&
+ (cdb->MODE_SENSE.PageCode == MODE_SENSE_RETURN_ALL)) {
+
+ //
+ // Mode sense for all pages failed. This command could fail with
+ // SCSI_SENSE_ILLEGAL_REQUEST / SCSI_ADSENSE_INVALID_CDB if the data
+ // to be returned is more than 256 bytes. In which case, try to get
+ // only MODE_PAGE_CACHING since we only need the block descriptor.
+ //
+ // Simply change the page code and retry the request
+ //
+
+ cdb->MODE_SENSE.PageCode = MODE_PAGE_CACHING;
+ *Retry = TRUE;
+ }
+
+ break;
+ }
+ } // end switch(asc)
+ break;
+ }
+
+ case SCSI_SENSE_NOT_READY: {
+
+ switch (asc) {
+ case SCSI_ADSENSE_LUN_NOT_READY: {
+ switch (ascq) {
+ case SCSI_SENSEQ_BECOMING_READY:
+ case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
+ case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+ } // end switch(ascq)
+ break;
+ }
+
+ case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+ } // end switch(asc)
+ break;
+ }
+
+ case SCSI_SENSE_MEDIUM_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_HARDWARE_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_UNIT_ATTENTION:
+ {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SCSI_SENSE_RECOVERED_ERROR: {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ } // end switch(senseKey)
+ } // end if (validSense)
+ } else {
+
+ //
+ // On any exceptional scsi condition which might indicate that the
+ // device was changed we will flush out the state of the partition
+ // table.
+ //
+
+ switch (SRB_STATUS(Srb->SrbStatus)) {
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_INVALID_TARGET_ID:
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_INVALID_PATH_ID:
+ case SRB_STATUS_COMMAND_TIMEOUT:
+ case SRB_STATUS_TIMEOUT:
+ case SRB_STATUS_SELECTION_TIMEOUT:
+ case SRB_STATUS_REQUEST_FLUSHED:
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+ case SRB_STATUS_PARITY_ERROR:
+ {
+ invalidatePartitionTable = TRUE;
+ break;
+ }
+
+ case SRB_STATUS_ERROR:
+ {
+ if (scsiStatus == SCSISTAT_RESERVATION_CONFLICT)
+ {
+ invalidatePartitionTable = TRUE;
+ }
+
+ break;
+ }
+ } // end switch(Srb->SrbStatus)
+ }
+
+ if (invalidatePartitionTable && TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Inform the upper layers that the volume
+ // on this disk is in need of verification
+ //
+
+ SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
+ }
+ }
+
+ return;
+}
+
+
+VOID
+DiskSetSpecialHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ )
+
+/*++
+
+Routine Description:
+
+ This function checks to see if an SCSI logical unit requires speical
+ flags to be set.
+
+Arguments:
+
+ Fdo - Supplies the device object to be tested.
+
+ InquiryData - Supplies the inquiry data returned by the device of interest.
+
+ AdapterDescriptor - Supplies the capabilities of the device object.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "Disk SetSpecialHacks, Setting Hacks %p\n", (void*) Data));
+
+ //
+ // Found a listed controller. Determine what must be done.
+ //
+
+ if (TEST_FLAG(Data, HackDisableTaggedQueuing)) {
+
+ //
+ // Disable tagged queuing.
+ //
+
+ CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ }
+
+ if (TEST_FLAG(Data, HackDisableSynchronousTransfers)) {
+
+ //
+ // Disable synchronous data transfers.
+ //
+
+ SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ }
+
+ if (TEST_FLAG(Data, HackDisableSpinDown)) {
+
+ //
+ // Disable spinning down of drives.
+ //
+
+ SET_FLAG(FdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_DOWN);
+
+ }
+
+ if (TEST_FLAG(Data, HackDisableWriteCache)) {
+
+ //
+ // Disable the drive's write cache
+ //
+
+ SET_FLAG(FdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_WRITE_CACHE);
+
+ }
+
+ if (TEST_FLAG(Data, HackCauseNotReportableHack)) {
+
+ SET_FLAG(FdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK);
+ }
+
+ if (TEST_FLAG(fdo->Characteristics, FILE_REMOVABLE_MEDIA) &&
+ TEST_FLAG(Data, HackRequiresStartUnitCommand)
+ ) {
+
+ //
+ // this is a list of vendors who require the START_UNIT command
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DiskScanForSpecial (%p) => This unit requires "
+ " START_UNITS\n", fdo));
+ SET_FLAG(FdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+
+ }
+
+ return;
+}
+
+
+VOID
+ResetBus(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ This command sends a reset bus command to the SCSI port driver.
+
+Arguments:
+
+ Fdo - The functional device object for the logical unit with hardware problem.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb;
+ PCOMPLETION_CONTEXT context;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "Disk ResetBus: Sending reset bus request to port driver.\n"));
+
+ //
+ // Allocate Srb from nonpaged pool.
+ //
+
+ context = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(COMPLETION_CONTEXT),
+ DISK_TAG_CCONTEXT);
+
+ if(context == NULL) {
+ return;
+ }
+
+ //
+ // Save the device object in the context for use by the completion
+ // routine.
+ //
+
+ context->DeviceObject = Fdo;
+ srb = &context->Srb.Srb;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = &context->Srb.SrbEx;
+
+ //
+ // Zero out srb
+ //
+
+ RtlZeroMemory(srbEx, sizeof(context->Srb.SrbExBuffer));
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(context->Srb.SrbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_RESET_BUS;
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ } else {
+
+ //
+ // Zero out srb.
+ //
+
+ RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
+
+ //
+ // Write length to SRB.
+ //
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+
+ srb->Function = SRB_FUNCTION_RESET_BUS;
+
+ }
+
+ //
+ // Build the asynchronous request to be sent to the port driver.
+ // Since this routine is called from a DPC the IRP should always be
+ // available.
+ //
+
+ irp = IoAllocateIrp(Fdo->StackSize, FALSE);
+
+ if (irp == NULL) {
+ FREE_POOL(context);
+ return;
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->OriginalRequest = irp;
+ } else {
+ srb->OriginalRequest = irp;
+ }
+
+ //
+ // Store the SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // Call the port driver with the IRP.
+ //
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ return;
+
+} // end ResetBus()
+
+
+
+VOID
+DiskLogCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo,
+ IN NTSTATUS Status
+ )
+{
+ PIO_ERROR_LOG_PACKET logEntry = NULL;
+
+ PAGED_CODE();
+
+ logEntry = IoAllocateErrorLogEntry(FdoExtension->DeviceObject, sizeof(IO_ERROR_LOG_PACKET) + (4 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+ BOOLEAN bIsEnabled = TEST_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+
+ logEntry->FinalStatus = Status;
+ logEntry->ErrorCode = (bIsEnabled) ? IO_WRITE_CACHE_ENABLED : IO_WRITE_CACHE_DISABLED;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode = IRP_MJ_SCSI;
+ logEntry->IoControlCode = 0;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = 0x1;
+ logEntry->DumpDataSize = 4 * sizeof(ULONG);
+
+ logEntry->DumpData[0] = diskData->ScsiAddress.PathId;
+ logEntry->DumpData[1] = diskData->ScsiAddress.TargetId;
+ logEntry->DumpData[2] = diskData->ScsiAddress.Lun;
+ logEntry->DumpData[3] = CacheInfo->WriteCacheEnabled;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+}
+
+NTSTATUS
+DiskGetInfoExceptionInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMODE_INFO_EXCEPTIONS ReturnPageData
+ )
+{
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_INFO_EXCEPTIONS pageData;
+ ULONG length;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // ReturnPageData is allocated by the caller
+ //
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_INFO_EXCEPTION);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_FAULT_REPORTING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_FAULT_REPORTING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: Mode Sense failed\n"));
+ FREE_POOL(modeData);
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (length > (ULONG) (modeData->ModeDataLength + 1)) {
+ length = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Find the mode page for info exceptions
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ length,
+ MODE_PAGE_FAULT_REPORTING,
+ TRUE);
+
+ if (pageData != NULL) {
+ RtlCopyMemory(ReturnPageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskGetInfoExceptionInformation: %s support SMART for device %p\n",
+ NT_SUCCESS(status) ? "does" : "does not",
+ FdoExtension->DeviceObject));
+
+ FREE_POOL(modeData);
+
+ return(status);
+}
+
+
+NTSTATUS
+DiskSetInfoExceptionInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMODE_INFO_EXCEPTIONS PageData
+ )
+
+{
+ ULONG i;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // We will attempt (twice) to issue the mode select with the page.
+ // Make the setting persistant so that we don't have to turn it back
+ // on after a bus reset.
+ //
+
+ for (i = 0; i < 2; i++)
+ {
+ status = DiskModeSelect(FdoExtension->DeviceObject,
+ (PCHAR) PageData,
+ sizeof(MODE_INFO_EXCEPTIONS),
+ TRUE);
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskSetInfoExceptionInformation: %s for device %p\n",
+ NT_SUCCESS(status) ? "succeeded" : "failed",
+ FdoExtension->DeviceObject));
+
+ return status;
+}
+
+NTSTATUS
+DiskGetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ )
+/*++
+
+Routine Description:
+
+ This function gets the caching mode page from the drive. This function
+ is called from DiskIoctlGetCacheInformation() in response to the IOCTL
+ IOCTL_DISK_GET_CACHE_INFORMATION. This is also called from the
+ DisableWriteCache() worker thread to disable caching when write commands fail.
+
+Arguments:
+
+ FdoExtension - The device extension for this device.
+
+ CacheInfo - Buffer to receive the Cache Information.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_CACHING_PAGE pageData;
+
+ ULONG length;
+
+ PAGED_CODE();
+
+
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_DISABLE_CACHE);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetSetCacheInformation: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetCacheInformation: Mode Sense failed\n"));
+
+ FREE_POOL(modeData);
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (length > (ULONG) (modeData->ModeDataLength + 1)) {
+ length = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Check to see if the write cache is enabled.
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskGetCacheInformation: Unable to find caching mode page.\n"));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Copy the parameters over.
+ //
+
+ RtlZeroMemory(CacheInfo, sizeof(DISK_CACHE_INFORMATION));
+
+ CacheInfo->ParametersSavable = pageData->PageSavable;
+
+ CacheInfo->ReadCacheEnabled = !(pageData->ReadDisableCache);
+ CacheInfo->WriteCacheEnabled = pageData->WriteCacheEnable;
+
+
+ //
+ // Translate the values in the mode page into the ones defined in
+ // ntdddisk.h.
+ //
+
+ CacheInfo->ReadRetentionPriority =
+ TRANSLATE_RETENTION_PRIORITY(pageData->ReadRetensionPriority);
+ CacheInfo->WriteRetentionPriority =
+ TRANSLATE_RETENTION_PRIORITY(pageData->WriteRetensionPriority);
+
+ CacheInfo->DisablePrefetchTransferLength =
+ ((pageData->DisablePrefetchTransfer[0] << 8) +
+ pageData->DisablePrefetchTransfer[1]);
+
+ CacheInfo->ScalarPrefetch.Minimum =
+ ((pageData->MinimumPrefetch[0] << 8) + pageData->MinimumPrefetch[1]);
+
+ CacheInfo->ScalarPrefetch.Maximum =
+ ((pageData->MaximumPrefetch[0] << 8) + pageData->MaximumPrefetch[1]);
+
+ if(pageData->MultiplicationFactor) {
+ CacheInfo->PrefetchScalar = TRUE;
+ CacheInfo->ScalarPrefetch.MaximumBlocks =
+ ((pageData->MaximumPrefetchCeiling[0] << 8) +
+ pageData->MaximumPrefetchCeiling[1]);
+ }
+
+
+ FREE_POOL(modeData);
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DiskSetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ )
+/*++
+
+Routine Description:
+
+ This function sets the caching mode page in the drive. This function
+ is also called from the DisableWriteCache() worker thread to disable
+ caching when write commands fail.
+
+Arguments:
+
+ FdoExtension - The device extension for this device.
+
+ CacheInfo - Buffer the contains the Cache Information to be set on the drive.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+{
+ PMODE_PARAMETER_HEADER modeData;
+ ULONG length;
+ PMODE_CACHING_PAGE pageData;
+ ULONG i;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_DISABLE_CACHE);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskSetCacheInformation: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, MODE_DATA_SIZE);
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ length = ClassModeSense(FdoExtension->DeviceObject,
+ (PCHAR) modeData,
+ MODE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskSetCacheInformation: Mode Sense failed\n"));
+
+ FREE_POOL(modeData);
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (length > (ULONG) (modeData->ModeDataLength + 1)) {
+ length = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Check to see if the write cache is enabled.
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Don't touch any of the normal parameters - not all drives actually
+ // use the correct size of caching mode page. Just change the things
+ // which the user could have modified.
+ //
+
+ pageData->PageSavable = FALSE;
+
+ pageData->ReadDisableCache = !(CacheInfo->ReadCacheEnabled);
+ pageData->MultiplicationFactor = CacheInfo->PrefetchScalar;
+ pageData->WriteCacheEnable = CacheInfo->WriteCacheEnabled;
+
+ pageData->WriteRetensionPriority = (UCHAR)
+ TRANSLATE_RETENTION_PRIORITY(CacheInfo->WriteRetentionPriority);
+ pageData->ReadRetensionPriority = (UCHAR)
+ TRANSLATE_RETENTION_PRIORITY(CacheInfo->ReadRetentionPriority);
+
+ pageData->DisablePrefetchTransfer[0] =
+ (UCHAR) (CacheInfo->DisablePrefetchTransferLength >> 8);
+ pageData->DisablePrefetchTransfer[1] =
+ (UCHAR) (CacheInfo->DisablePrefetchTransferLength & 0x00ff);
+
+ pageData->MinimumPrefetch[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Minimum >> 8);
+ pageData->MinimumPrefetch[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Minimum & 0x00ff);
+
+ pageData->MaximumPrefetch[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Maximum >> 8);
+ pageData->MaximumPrefetch[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.Maximum & 0x00ff);
+
+ if(pageData->MultiplicationFactor) {
+
+ pageData->MaximumPrefetchCeiling[0] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks >> 8);
+ pageData->MaximumPrefetchCeiling[1] =
+ (UCHAR) (CacheInfo->ScalarPrefetch.MaximumBlocks & 0x00ff);
+ }
+
+ //
+ // We will attempt (twice) to issue the mode select with the page.
+ //
+
+ for (i = 0; i < 2; i++) {
+
+ status = DiskModeSelect(FdoExtension->DeviceObject,
+ (PCHAR) pageData,
+ (pageData->PageLength + 2),
+ CacheInfo->ParametersSavable);
+
+ if (NT_SUCCESS(status)) {
+
+ if (CacheInfo->WriteCacheEnabled)
+ {
+ SET_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ else
+ {
+ CLEAR_FLAG(FdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ ADJUST_FUA_FLAG(FdoExtension);
+
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ } else {
+
+ //
+ // We were unable to modify the disk write cache setting
+ //
+
+ SET_FLAG(FdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL);
+ }
+
+ FREE_POOL(modeData);
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine services IOCTL_DISK_GET_CACHE_SETTING. It looks to
+ see if there are any issues with the disk cache and whether the
+ user had previously indicated that the cache is power-protected
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The ioctl to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_CACHE_SETTING))
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else
+ {
+ PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
+
+ cacheSetting->Version = sizeof(DISK_CACHE_SETTING);
+ cacheSetting->State = DiskCacheNormal;
+
+ //
+ // Determine whether it is safe to turn on the cache
+ //
+ if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED))
+ {
+ cacheSetting->State = DiskCacheWriteThroughNotSupported;
+ }
+
+ //
+ // Determine whether it is possible to modify the cache setting
+ //
+ if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_MODIFY_CACHE_UNSUCCESSFUL))
+ {
+ cacheSetting->State = DiskCacheModifyUnsuccessful;
+ }
+
+ cacheSetting->IsPowerProtected = TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+
+ Irp->IoStatus.Information = sizeof(DISK_CACHE_SETTING);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskIoctlSetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine services IOCTL_DISK_SET_CACHE_SETTING. It allows
+ the user to specify whether the disk cache is power-protected
+ or not
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The ioctl to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_CACHE_SETTING))
+ {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ else
+ {
+ PDISK_CACHE_SETTING cacheSetting = (PDISK_CACHE_SETTING)Irp->AssociatedIrp.SystemBuffer;
+
+ if (cacheSetting->Version == sizeof(DISK_CACHE_SETTING))
+ {
+ ULONG isPowerProtected;
+
+ //
+ // Save away the user-defined override in our extension and the registry
+ //
+ if (cacheSetting->IsPowerProtected)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+ isPowerProtected = 1;
+ }
+ else
+ {
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+ isPowerProtected = 0;
+ }
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, isPowerProtected);
+ }
+ else
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetLengthInfo(
+ IN OUT PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_LENGTH_INFO. It returns
+ the disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpStack;
+ PGET_LENGTH_INFORMATION lengthInfo;
+ PFUNCTIONAL_DEVICE_EXTENSION p0Extension;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PDISK_DATA partitionZeroData;
+ NTSTATUS oldReadyStatus;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Initialization
+ //
+
+ commonExtension = DeviceObject->DeviceExtension;
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+ p0Extension = commonExtension->PartitionZeroExtension;
+ partitionZeroData = ((PDISK_DATA) p0Extension->CommonExtension.DriverData);
+
+ //
+ // Check that the buffer is large enough.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Update the geometry in case it has changed
+ //
+
+ status = DiskReadDriveCapacity(p0Extension->DeviceObject);
+
+ //
+ // Note whether the drive is ready. If the status has changed then
+ // notify pnp.
+ //
+
+ oldReadyStatus = InterlockedExchange(&(partitionZeroData->ReadyStatus), status);
+
+ if(partitionZeroData->ReadyStatus != oldReadyStatus) {
+ IoInvalidateDeviceRelations(p0Extension->LowerPdo, BusRelations);
+ }
+
+ if(!NT_SUCCESS(status)) {
+ return status;
+ }
+ lengthInfo = (PGET_LENGTH_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
+
+ lengthInfo->Length = commonExtension->PartitionLength;
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetDriveGeometry(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY. It returns
+ the disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - IRP with a return buffer large enough to receive the
+ extended geometry information.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetDriveGeometry: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Issue ReadCapacity to update device extension
+ // with information for current media.
+ //
+
+ status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ //
+ // Note whether the drive is ready.
+ //
+
+ diskData->ReadyStatus = status;
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+ }
+
+ //
+ // Copy drive geometry information from device extension.
+ //
+
+ RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
+ &(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ if (((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector == 0) {
+ ((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector = 512;
+ }
+ Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+ return STATUS_SUCCESS;
+}
+
+typedef struct _DISK_GEOMETRY_EX_INTERNAL {
+ DISK_GEOMETRY Geometry;
+ LARGE_INTEGER DiskSize;
+ DISK_PARTITION_INFO Partition;
+ DISK_DETECTION_INFO Detection;
+} DISK_GEOMETRY_EX_INTERNAL, *PDISK_GEOMETRY_EX_INTERNAL;
+
+NTSTATUS
+DiskIoctlGetDriveGeometryEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. It returns
+ the extended disk geometry to the caller.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - The device object to obtain the geometry for.
+
+ Irp - IRP with a return buffer large enough to receive the
+ extended geometry information.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpStack;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PDISK_DATA diskData;
+ PDISK_GEOMETRY_EX_INTERNAL geometryEx;
+ ULONG OutputBufferLength;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Setup parameters
+ //
+
+ commonExtension = DeviceObject->DeviceExtension;
+ fdoExtension = DeviceObject->DeviceExtension;
+ diskData = (PDISK_DATA)(commonExtension->DriverData);
+ irpStack = IoGetCurrentIrpStackLocation ( Irp );
+ geometryEx = NULL;
+ OutputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ //
+ // Check that the buffer is large enough. It must be large enough
+ // to hold at lest the Geometry and DiskSize fields of of the
+ // DISK_GEOMETRY_EX structure.
+ //
+
+ if ( (LONG)OutputBufferLength < FIELD_OFFSET (DISK_GEOMETRY_EX, Data) ) {
+
+ //
+ // Buffer too small. Bail out, telling the caller the required
+ // size.
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetDriveGeometryEx: Output buffer too small.\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ return status;
+ }
+
+ if (TEST_FLAG (DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ //
+ // Issue a ReadCapacity to update device extension
+ // with information for the current media.
+ //
+
+ status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ diskData->ReadyStatus = status;
+
+ if (!NT_SUCCESS (status)) {
+ return status;
+ }
+ }
+
+ //
+ // Copy drive geometry.
+ //
+
+ geometryEx = (PDISK_GEOMETRY_EX_INTERNAL)Irp->AssociatedIrp.SystemBuffer;
+ geometryEx->Geometry = fdoExtension->DiskGeometry;
+ if (geometryEx->Geometry.BytesPerSector == 0) {
+ geometryEx->Geometry.BytesPerSector = 512;
+ }
+ geometryEx->DiskSize = commonExtension->PartitionZeroExtension->CommonExtension.PartitionLength;
+
+ //
+ // If the user buffer is large enough to hold the partition information
+ // then add that as well.
+ //
+
+ if ((LONG)OutputBufferLength >= FIELD_OFFSET (DISK_GEOMETRY_EX_INTERNAL, Detection)) {
+
+ geometryEx->Partition.SizeOfPartitionInfo = sizeof (geometryEx->Partition);
+ geometryEx->Partition.PartitionStyle = diskData->PartitionStyle;
+
+ switch ( diskData->PartitionStyle ) {
+
+ case PARTITION_STYLE_GPT:
+
+ //
+ // Copy GPT signature.
+ //
+
+ geometryEx->Partition.Gpt.DiskId = diskData->Efi.DiskId;
+ break;
+
+ case PARTITION_STYLE_MBR:
+
+ //
+ // Copy MBR signature and checksum.
+ //
+
+ geometryEx->Partition.Mbr.Signature = diskData->Mbr.Signature;
+ geometryEx->Partition.Mbr.CheckSum = diskData->Mbr.MbrCheckSum;
+ break;
+
+ default:
+
+ //
+ // This is a raw disk. Zero out the signature area so
+ // nobody gets confused.
+ //
+
+ RtlZeroMemory(&geometryEx->Partition, sizeof (geometryEx->Partition));
+ }
+ }
+
+ //
+ // If the buffer is large enough to hold the detection information,
+ // then also add that.
+ //
+
+ if (OutputBufferLength >= sizeof (DISK_GEOMETRY_EX_INTERNAL)) {
+
+ geometryEx->Detection.SizeOfDetectInfo = sizeof (geometryEx->Detection);
+
+ status = DiskGetDetectInfo(fdoExtension, &geometryEx->Detection);
+
+ //
+ // Failed to obtain detection information, set to none.
+ //
+
+ if (!NT_SUCCESS (status)) {
+ geometryEx->Detection.DetectionType = DetectNone;
+ }
+ }
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = min (OutputBufferLength, sizeof (DISK_GEOMETRY_EX_INTERNAL));
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_GET_CACHE_INFORMATION. It reads
+ the caching mode page from the device and returns information to
+ the caller. After validating the user parameter it calls the
+ DiskGetCacheInformation() function to get the mode page.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_CACHE_INFORMATION)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetCacheInformation: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ status = DiskGetCacheInformation(fdoExtension, cacheInfo);
+
+ if (NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = sizeof(DISK_CACHE_INFORMATION);
+
+ //
+ // Make sure write cache setting is reflected in device extension
+ //
+ if (cacheInfo->WriteCacheEnabled)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ else
+ {
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ }
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ }
+ return status;
+}
+
+
+NTSTATUS
+DiskIoctlSetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_SET_CACHE_INFORMATION. It allows
+ the caller to set the caching mode page on the device. This function
+ validates the user parameter and calls the DiskSetCacheInformation()
+ function to set the mode page. It also stores the cache value in the
+ device extension and registry.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PDISK_CACHE_INFORMATION cacheInfo = Irp->AssociatedIrp.SystemBuffer;
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL is equal or above DISPATCH_LEVEL.
+ //
+
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSetCacheInformation: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DISK_CACHE_INFORMATION)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSetCacheInformation: Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ status = DiskSetCacheInformation(fdoExtension, cacheInfo);
+
+ //
+ // Save away the user-defined override in our extension and the registry
+ //
+ if (cacheInfo->WriteCacheEnabled) {
+ diskData->WriteCacheOverride = DiskWriteCacheEnable;
+ } else {
+ diskData->WriteCacheOverride = DiskWriteCacheDisable;
+ }
+
+ ClassSetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey,
+ DiskDeviceUserWriteCacheSetting, diskData->WriteCacheOverride);
+
+ DiskLogCacheInformation(fdoExtension, cacheInfo, status);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetMediaTypesEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_GET_MEDIA_TYPES_EX. It returns
+ the media type information to the caller. After validating the user
+ parameter it calls DiskDetermineMediaTypes() to get the media type.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_PARAMETER_BLOCK blockDescriptor;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG modeLength;
+ ULONG retries = 4;
+ UCHAR densityCode = 0;
+ BOOLEAN writable = TRUE;
+ BOOLEAN mediaPresent = FALSE;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_MEDIA_TYPES)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Send a TUR to determine if media is present.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Insufficient extended SRB size.\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+ cdb = (PCDB)srb->Cdb;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ }
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ NULL,
+ 0,
+ FALSE);
+
+ if (NT_SUCCESS(status)) {
+ mediaPresent = TRUE;
+ }
+
+ modeLength = MODE_DATA_SIZE;
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ modeLength,
+ DISK_TAG_MODE_DATA);
+
+ if (modeData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Unable to allocate memory.\n"));
+ FREE_POOL(srb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, modeLength);
+
+ //
+ // Build the MODE SENSE CDB using previous SRB.
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx->SrbStatus = 0;
+ srbExDataCdb16->ScsiStatus = 0;
+ srbExDataCdb16->CdbLength = 6;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+ } else {
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value from device extension.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+ }
+
+ //
+ // Page code of 0x3F will return all pages.
+ // This command could fail if the data to be returned is
+ // more than 256 bytes. In which case, we should get only
+ // the caching page since we only need the block descriptor.
+ // DiskFdoProcessError will change the page code to
+ // MODE_PAGE_CACHING if there is an error.
+ //
+
+ cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ cdb->MODE_SENSE.PageCode = MODE_SENSE_RETURN_ALL;
+ cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;
+
+Retry:
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ modeData,
+ modeLength,
+ FALSE);
+
+ if (status == STATUS_VERIFY_REQUIRED) {
+
+ if (retries--) {
+
+ //
+ // Retry request.
+ //
+
+ goto Retry;
+ }
+ } else if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {
+
+ //
+ // Get the block descriptor.
+ //
+
+ if (modeData->BlockDescriptorLength != 0) {
+
+ blockDescriptor = (PMODE_PARAMETER_BLOCK)((ULONG_PTR)modeData + sizeof(MODE_PARAMETER_HEADER));
+ densityCode = blockDescriptor->DensityCode;
+ }
+
+ if (TEST_FLAG(modeData->DeviceSpecificParameter,
+ MODE_DSP_WRITE_PROTECT)) {
+
+ writable = FALSE;
+ }
+
+ status = DiskDetermineMediaTypes(DeviceObject,
+ Irp,
+ modeData->MediumType,
+ densityCode,
+ mediaPresent,
+ writable);
+ //
+ // If the buffer was too small, DetermineMediaTypes updated the status and information and the request will fail.
+ //
+
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetMediaTypesEx: Mode sense for header/bd failed. %lx\n", status));
+ }
+
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlPredictFailure(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_PREDICT_FAILURE. If the device
+ supports SMART then it returns any available failure data.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PSTORAGE_PREDICT_FAILURE checkFailure;
+ STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ KEVENT event;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_PREDICT_FAILURE)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlPredictFailure: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // See if the disk is predicting failure
+ //
+
+ checkFailure = (PSTORAGE_PREDICT_FAILURE)Irp->AssociatedIrp.SystemBuffer;
+
+ if (diskData->FailurePredictionCapability == FailurePredictionSense) {
+ ULONG readBufferSize;
+ PUCHAR readBuffer;
+ PIRP readIrp;
+ PDEVICE_OBJECT topOfStack;
+
+ checkFailure->PredictFailure = 0;
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ topOfStack = IoGetAttachedDeviceReference(DeviceObject);
+
+ //
+ // SCSI disks need to have a read sent down to provoke any
+ // failures to be reported.
+ //
+ // Issue a normal read operation. The error-handling code in
+ // classpnp will take care of a failure prediction by logging the
+ // correct event.
+ //
+
+ readBufferSize = fdoExtension->DiskGeometry.BytesPerSector;
+ readBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ readBufferSize,
+ DISK_TAG_SMART);
+
+ if (readBuffer != NULL) {
+ LARGE_INTEGER offset;
+
+ offset.QuadPart = 0;
+ readIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
+ topOfStack,
+ readBuffer,
+ readBufferSize,
+ &offset,
+ &event,
+ &ioStatus);
+
+ if (readIrp != NULL) {
+
+ status = IoCallDriver(topOfStack, readIrp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ FREE_POOL(readBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ObDereferenceObject(topOfStack);
+ }
+
+ if (status != STATUS_INSUFFICIENT_RESOURCES)
+ {
+ if ((diskData->FailurePredictionCapability == FailurePredictionSmart) ||
+ (diskData->FailurePredictionCapability == FailurePredictionSense)) {
+
+ status = DiskReadFailurePredictStatus(fdoExtension, &diskSmartStatus);
+
+ if (NT_SUCCESS(status)) {
+
+ status = DiskReadFailurePredictData(fdoExtension,
+ Irp->AssociatedIrp.SystemBuffer);
+
+ if (diskSmartStatus.PredictFailure) {
+ checkFailure->PredictFailure = 1;
+ } else {
+ checkFailure->PredictFailure = 0;
+ }
+
+ Irp->IoStatus.Information = sizeof(STORAGE_PREDICT_FAILURE);
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ }
+ return status;
+}
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+NTSTATUS
+DiskIoctlEnableFailurePrediction(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_FAILURE_PREDICTION_CONFIG. If the device
+ supports SMART then it returns any available failure data.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+ PSTORAGE_FAILURE_PREDICTION_CONFIG enablePrediction;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG) ||
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ enablePrediction = (PSTORAGE_FAILURE_PREDICTION_CONFIG)Irp->AssociatedIrp.SystemBuffer;
+
+ if (enablePrediction->Version != STORAGE_FAILURE_PREDICTION_CONFIG_V1 ||
+ enablePrediction->Size < sizeof(STORAGE_FAILURE_PREDICTION_CONFIG)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Buffer version or size is incorrect.\n"));
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if (enablePrediction->Reserved != 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlEnableFailurePrediction: Reserved bytes are not zero!\n"));
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Default to success. This might get overwritten on failure below.
+ //
+ status = STATUS_SUCCESS;
+
+ //
+ // If this is a "set" and the current state (enabled/disabled) is
+ // different from the sender's desired state,
+ //
+ if (enablePrediction->Set && enablePrediction->Enabled != diskData->FailurePredictionEnabled) {
+ if (diskData->FailurePredictionCapability == FailurePredictionSmart ||
+ diskData->FailurePredictionCapability == FailurePredictionIoctl) {
+ //
+ // SMART or IOCTL based failure prediction is being used so call
+ // the generic function that is normally called in the WMI path.
+ //
+ status = DiskEnableDisableFailurePrediction(fdoExtension, enablePrediction->Enabled);
+ } else if (diskData->ScsiInfoExceptionsSupported) {
+ //
+ // If we know that the device supports the Informational Exceptions
+ // mode page, try to enable/disable failure prediction that way.
+ //
+ status = DiskEnableInfoExceptions(fdoExtension, enablePrediction->Enabled);
+ }
+ }
+
+ //
+ // Return the current state regardless if this was a "set" or a "get".
+ //
+ enablePrediction->Enabled = diskData->FailurePredictionEnabled;
+
+ if (NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = sizeof(STORAGE_FAILURE_PREDICTION_CONFIG);
+ }
+
+ return status;
+}
+#endif //(NTDDI_VERSION >= NTDDI_WINBLUE)
+
+NTSTATUS
+DiskIoctlVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_VERIFY. After verifying
+ user input, it starts the worker thread DiskIoctlVerifyThread()
+ to verify the device.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
+ PDISK_VERIFY_WORKITEM_CONTEXT Context = NULL;
+ PSCSI_REQUEST_BLOCK srb;
+ LARGE_INTEGER byteOffset;
+ ULONG srbSize;
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VERIFY_INFORMATION)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Add disk offset to starting sector.
+ //
+
+ byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart +
+ verifyInfo->StartingOffset.QuadPart;
+
+ //
+ // Perform a bounds check on the sector range
+ //
+
+ if ((verifyInfo->StartingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
+ (verifyInfo->StartingOffset.QuadPart < 0)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Verify request to invalid sector.\n"));
+ FREE_POOL(srb)
+ return STATUS_NONEXISTENT_SECTOR;
+ } else {
+
+ ULONGLONG bytesRemaining = commonExtension->PartitionLength.QuadPart - verifyInfo->StartingOffset.QuadPart;
+
+ if ((ULONGLONG)verifyInfo->Length > bytesRemaining) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlVerify: Verify request to invalid sector.\n"));
+ FREE_POOL(srb)
+ return STATUS_NONEXISTENT_SECTOR;
+ }
+ }
+
+ Context = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(DISK_VERIFY_WORKITEM_CONTEXT),
+ DISK_TAG_WI_CONTEXT);
+ if (Context) {
+
+ Context->Irp = Irp;
+ Context->Srb = srb;
+ Context->WorkItem = IoAllocateWorkItem(DeviceObject);
+
+ if (Context->WorkItem) {
+
+ //
+ // Queue the work item and return.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ IoQueueWorkItem(Context->WorkItem,
+ DiskIoctlVerifyThread,
+ DelayedWorkQueue,
+ Context);
+
+ return STATUS_PENDING;
+ }
+ FREE_POOL(Context);
+ }
+ FREE_POOL(srb)
+ return STATUS_INSUFFICIENT_RESOURCES;
+}
+
+NTSTATUS
+DiskIoctlReassignBlocks(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_REASSIGN_BLOCKS. This IOCTL
+ allows the caller to remap the defective blocks to a new
+ location on the disk.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+ PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG bufferSize;
+ ULONG blockNumber;
+ ULONG blockCount;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(REASSIGN_BLOCKS)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Make sure we have some data in the input buffer.
+ //
+
+ if (badBlocks->Count == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Invalid block count\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ bufferSize = sizeof(REASSIGN_BLOCKS) + ((badBlocks->Count - 1) * sizeof(ULONG));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < bufferSize) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Input buffer length mismatch for bad blocks.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Build the data buffer to be transferred in the input buffer.
+ // The format of the data to the device is:
+ //
+ // 2 bytes Reserved
+ // 2 bytes Length
+ // x * 4 btyes Block Address
+ //
+ // All values are big endian.
+ //
+
+ badBlocks->Reserved = 0;
+ blockCount = badBlocks->Count;
+
+ //
+ // Convert # of entries to # of bytes.
+ //
+
+ blockCount *= 4;
+ badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
+ badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
+
+ //
+ // Convert back to number of entries.
+ //
+
+ blockCount /= 4;
+
+ for (; blockCount > 0; blockCount--) {
+
+ blockNumber = badBlocks->BlockNumber[blockCount-1];
+ REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1], (PFOUR_BYTE) &blockNumber);
+ }
+
+ //
+ // Build a SCSI SRB containing a SCSIOP_REASSIGN_BLOCKS cdb
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Insufficient extended SRB size.\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ cdb = (PCDB)srb->Cdb;
+ }
+
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ badBlocks,
+ bufferSize,
+ TRUE);
+
+ FREE_POOL(srb);
+ return status;
+}
+
+NTSTATUS
+DiskIoctlReassignBlocksEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_REASSIGN_BLOCKS_EX. This IOCTL
+ allows the caller to remap the defective blocks to a new
+ location on the disk. The input buffer contains 8-byte LBAs.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+ PREASSIGN_BLOCKS_EX badBlocks = Irp->AssociatedIrp.SystemBuffer;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ LARGE_INTEGER blockNumber;
+ ULONG bufferSize;
+ ULONG blockCount;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(REASSIGN_BLOCKS_EX)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Input buffer length mismatch.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Make sure we have some data in the input buffer.
+ //
+
+ if (badBlocks->Count == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Invalid block count\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ bufferSize = sizeof(REASSIGN_BLOCKS_EX) + ((badBlocks->Count - 1) * sizeof(LARGE_INTEGER));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < bufferSize) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocksEx: Input buffer length mismatch for bad blocks.\n"));
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Build the data buffer to be transferred in the input buffer.
+ // The format of the data to the device is:
+ //
+ // 2 bytes Reserved
+ // 2 bytes Length
+ // x * 8 btyes Block Address
+ //
+ // All values are big endian.
+ //
+
+ badBlocks->Reserved = 0;
+ blockCount = badBlocks->Count;
+
+ //
+ // Convert # of entries to # of bytes.
+ //
+
+ blockCount *= 8;
+ badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
+ badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
+
+ //
+ // Convert back to number of entries.
+ //
+
+ blockCount /= 8;
+
+ for (; blockCount > 0; blockCount--) {
+
+ blockNumber = badBlocks->BlockNumber[blockCount-1];
+ REVERSE_BYTES_QUAD(&badBlocks->BlockNumber[blockCount-1], &blockNumber);
+ }
+
+ //
+ // Build a SCSI SRB containing a SCSIOP_REASSIGN_BLOCKS cdb
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlReassignBlocks: Insufficient extended SRB size.\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ cdb = (PCDB)srb->Cdb;
+ }
+
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
+ cdb->CDB6GENERIC.CommandUniqueBits = 1; // LONGLBA
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ badBlocks,
+ bufferSize,
+ TRUE);
+
+ FREE_POOL(srb);
+ return status;
+}
+
+NTSTATUS
+DiskIoctlIsWritable(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_IS_WRITABLE. This function
+ returns whether the disk is writable. If the device is not
+ writable then STATUS_MEDIA_WRITE_PROTECTED will be returned.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PMODE_PARAMETER_HEADER modeData;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb = NULL;
+ ULONG modeLength;
+ ULONG retries = 4;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+
+ if (srb == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Allocate memory for a mode header and then some
+ // for port drivers that need to convert to MODE10
+ // or always return the MODE_PARAMETER_BLOCK (even
+ // when memory was not allocated for this purpose)
+ //
+
+ modeLength = MODE_DATA_SIZE;
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ modeLength,
+ DISK_TAG_MODE_DATA);
+
+ if (modeData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlIsWritable: Unable to allocate memory.\n"));
+ FREE_POOL(srb);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(modeData, modeLength);
+
+ //
+ // Build the MODE SENSE CDB
+ //
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(Irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value.
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->CdbLength = 6;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ cdb = (PCDB)srb->Cdb;
+ }
+
+ //
+ // Page code of 0x3F will return all pages.
+ // This command could fail if the data to be returned is
+ // more than 256 bytes. In which case, we should get only
+ // the caching page since we only need the block descriptor.
+ // DiskFdoProcessError will change the page code to
+ // MODE_PAGE_CACHING if there is an error.
+ //
+
+ cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ cdb->MODE_SENSE.PageCode = MODE_SENSE_RETURN_ALL;
+ cdb->MODE_SENSE.AllocationLength = (UCHAR)modeLength;
+
+ while (retries != 0) {
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ srb,
+ modeData,
+ modeLength,
+ FALSE);
+
+ if (status != STATUS_VERIFY_REQUIRED) {
+ if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+ break;
+ }
+ retries--;
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ if (TEST_FLAG(modeData->DeviceSpecificParameter, MODE_DSP_WRITE_PROTECT)) {
+ status = STATUS_MEDIA_WRITE_PROTECTED;
+ }
+ }
+
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ return status;
+}
+
+NTSTATUS
+DiskIoctlSetVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_INTERNAL_SET_VERIFY.
+ This is an internal function used to set the DO_VERIFY_VOLUME
+ device object flag. Only a kernel mode component can send this
+ IOCTL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSetVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ //
+ // If the caller is kernel mode, set the verify bit.
+ //
+
+ if (Irp->RequestorMode == KernelMode) {
+
+ SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
+ status = STATUS_SUCCESS;
+
+ }
+ return status;
+}
+
+NTSTATUS
+DiskIoctlClearVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_INTERNAL_CLEAR_VERIFY.
+ This is an internal function used to clear the DO_VERIFY_VOLUME
+ device object flag. Only a kernel mode component can send this
+ IOCTL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlClearVerify: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ //
+ // If the caller is kernel mode, set the verify bit.
+ //
+
+ if (Irp->RequestorMode == KernelMode) {
+
+ CLEAR_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
+ status = STATUS_SUCCESS;
+
+ }
+ return status;
+}
+
+NTSTATUS
+DiskIoctlUpdateDriveSize(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_DISK_UPDATE_DRIVE_SIZE.
+ This function is used to inform the disk driver to update
+ the device geometry information cached in the device extension
+ This is normally initiated from the drivers layers above disk
+ driver.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ TARGET_DEVICE_CUSTOM_NOTIFICATION Notification = {0};
+ NTSTATUS status;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlUpdateDriveSize: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlUpdateDriveSize: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ status = DiskReadDriveCapacity(DeviceObject);
+
+ //
+ // Note whether the drive is ready.
+ //
+
+ diskData->ReadyStatus = status;
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Copy drive geometry information from the device extension.
+ //
+
+ RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
+ &(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ if (((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector == 0) {
+ ((PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer)->BytesPerSector = 512;
+ }
+ Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+ status = STATUS_SUCCESS;
+
+ //
+ // Notify everyone that the disk layout may have changed
+ //
+
+ Notification.Event = GUID_IO_DISK_LAYOUT_CHANGE;
+ Notification.Version = 1;
+ Notification.Size = (USHORT)FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
+ Notification.FileObject = NULL;
+ Notification.NameBufferOffset = -1;
+
+ IoReportTargetDeviceChangeAsynchronous(fdoExtension->LowerPdo,
+ &Notification,
+ NULL,
+ NULL);
+ }
+ return status;
+}
+
+NTSTATUS
+DiskIoctlGetVolumeDiskExtents(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
+ and IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS_ADMIN. This function
+ returns the physical location of a volume.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlGetVolumeDiskExtents: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+
+ if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_DISK_EXTENTS)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlGetVolumeDiskExtents: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ //
+ // Note whether the drive is ready.
+ //
+
+ diskData->ReadyStatus = status;
+
+ if (NT_SUCCESS(status)) {
+
+ PVOLUME_DISK_EXTENTS pVolExt = (PVOLUME_DISK_EXTENTS)Irp->AssociatedIrp.SystemBuffer;
+
+ pVolExt->NumberOfDiskExtents = 1;
+ pVolExt->Extents[0].DiskNumber = commonExtension->PartitionZeroExtension->DeviceNumber;
+ pVolExt->Extents[0].StartingOffset = commonExtension->StartingOffset;
+ pVolExt->Extents[0].ExtentLength = commonExtension->PartitionLength;
+
+ Irp->IoStatus.Information = sizeof(VOLUME_DISK_EXTENTS);
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlSmartGetVersion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services SMART_GET_VERSION. It returns the
+ SMART version information.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status;
+
+ PGETVERSIONINPARAMS versionParams;
+ PSRB_IO_CONTROL srbControl;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ PUCHAR buffer;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GETVERSIONINPARAMS)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(SRB_IO_CONTROL) +
+ sizeof(GETVERSIONINPARAMS),
+ DISK_TAG_SMART);
+
+ if (srbControl == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartGetVersion: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(srbControl, sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));
+
+ //
+ // fill in srbControl fields
+ //
+
+ srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+ RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
+ srbControl->Timeout = fdoExtension->TimeOutValue;
+ srbControl->Length = sizeof(GETVERSIONINPARAMS);
+ srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;
+
+ //
+ // Point to the 'buffer' portion of the SRB_CONTROL
+ //
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ //
+ // Ensure correct target is set in the cmd parameters.
+ //
+
+ versionParams = (PGETVERSIONINPARAMS)buffer;
+ versionParams->bIDEDeviceMap = diskData->ScsiAddress.TargetId;
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_SCSI_MINIPORT,
+ commonExtension->LowerDeviceObject,
+ srbControl,
+ sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
+ sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
+ FALSE,
+ &ioStatus);
+
+ status = ioStatus.Status;
+
+ //
+ // If successful, copy the data received into the output buffer.
+ // This should only fail in the event that the IDE driver is older
+ // than this driver.
+ //
+
+ if (NT_SUCCESS(status)) {
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ RtlMoveMemory (Irp->AssociatedIrp.SystemBuffer, buffer, sizeof(GETVERSIONINPARAMS));
+ Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
+ }
+
+ FREE_POOL(srbControl);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlSmartReceiveDriveData(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services SMART_RCV_DRIVE_DATA. This function
+ allows the caller to read SMART information from the device.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+ PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
+ PSRB_IO_CONTROL srbControl;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ ULONG controlCode = 0;
+ PUCHAR buffer;
+ PIRP irp2;
+ KEVENT event;
+ ULONG length = 0;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < (sizeof(SENDCMDINPARAMS) - 1)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Input buffer length invalid.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Create notification event object to be used to signal the
+ // request completion.
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ //
+ // use controlCode as a sort of 'STATUS_SUCCESS' to see if it's
+ // a valid request type
+ //
+
+ if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) {
+
+ length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
+ controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
+
+ } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
+
+ switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
+
+ case READ_ATTRIBUTES:
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
+ length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
+ break;
+
+ case READ_THRESHOLDS:
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
+ length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
+ break;
+
+ case SMART_READ_LOG: {
+
+ if (diskData->FailurePredictionCapability != FailurePredictionSmart) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: SMART failure prediction not supported.\n"));
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ //
+ // Calculate additional length based on number of sectors to be read.
+ // Then verify the output buffer is large enough.
+ //
+
+ length = cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
+
+ //
+ // Ensure at least 1 sector is going to be read
+ //
+ if (length == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ length += max(sizeof(SENDCMDOUTPARAMS), sizeof(SENDCMDINPARAMS));
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < length - 1) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Output buffer too small for SMART_READ_LOG.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
+ break;
+ }
+ }
+ }
+
+ if (controlCode == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Invalid request.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(SRB_IO_CONTROL) + length,
+ DISK_TAG_SMART);
+
+ if (srbControl == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // fill in srbControl fields
+ //
+
+ srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+ RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
+ srbControl->Timeout = fdoExtension->TimeOutValue;
+ srbControl->Length = length;
+ srbControl->ControlCode = controlCode;
+
+ //
+ // Point to the 'buffer' portion of the SRB_CONTROL
+ //
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ //
+ // Ensure correct target is set in the cmd parameters.
+ //
+
+ cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
+
+ //
+ // Copy the IOCTL parameters to the srb control buffer area.
+ //
+
+ RtlMoveMemory(buffer,
+ Irp->AssociatedIrp.SystemBuffer,
+ sizeof(SENDCMDINPARAMS) - 1);
+
+ irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
+ commonExtension->LowerDeviceObject,
+ srbControl,
+ sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
+ srbControl,
+ sizeof(SRB_IO_CONTROL) + length,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp2 == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartReceiveDriveData: Unable to allocate IRP.\n"));
+ FREE_POOL(srbControl);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ //
+ // Copy the data received into the output buffer. Since the status buffer
+ // contains error information also, always perform this copy. IO will
+ // either pass this back to the app, or zero it, in case of error.
+ //
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ if (NT_SUCCESS(status)) {
+
+ RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length - 1);
+ Irp->IoStatus.Information = length - 1;
+
+ } else {
+
+ RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, (sizeof(SENDCMDOUTPARAMS) - 1));
+ Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;
+
+ }
+
+ FREE_POOL(srbControl);
+
+ return status;
+}
+
+NTSTATUS
+DiskIoctlSmartSendDriveCommand(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services SMART_SEND_DRIVE_COMMAND. This function
+ allows the caller to send SMART commands to the device.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation (Irp);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+
+ PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
+ PSRB_IO_CONTROL srbControl;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ ULONG controlCode = 0;
+ PUCHAR buffer;
+ PIRP irp2;
+ KEVENT event;
+ ULONG length = 0;
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+ CHECK_IRQL();
+
+ //
+ // Validate the request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: DeviceObject %p Irp %p\n", DeviceObject, Irp));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < (sizeof(SENDCMDINPARAMS) - 1)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Input buffer size invalid.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < (sizeof(SENDCMDOUTPARAMS) - 1)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Output buffer too small.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Create notification event object to be used to signal the
+ // request completion.
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
+
+ switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
+
+ case SMART_WRITE_LOG: {
+
+ if (diskData->FailurePredictionCapability != FailurePredictionSmart) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: SMART failure prediction not supported.\n"));
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ //
+ // Calculate additional length based on number of sectors to be written.
+ // Then verify the input buffer is large enough.
+ //
+
+ length = cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE;
+
+ //
+ // Ensure at least 1 sector is going to be written
+ //
+
+ if (length == 0) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ (sizeof(SENDCMDINPARAMS) - 1) + length) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Input buffer too small for SMART_WRITE_LOG.\n"));
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
+ break;
+ }
+
+ case ENABLE_SMART:
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
+ break;
+
+ case DISABLE_SMART:
+ controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
+ break;
+
+ case RETURN_SMART_STATUS:
+
+ //
+ // Ensure bBuffer is at least 2 bytes (to hold the values of
+ // cylinderLow and cylinderHigh).
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
+ length = sizeof(IDEREGS);
+ break;
+
+ case ENABLE_DISABLE_AUTOSAVE:
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
+ break;
+
+ case SAVE_ATTRIBUTE_VALUES:
+ controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
+ break;
+
+ case EXECUTE_OFFLINE_DIAGS:
+ //
+ // Validate that this is an ok self test command
+ //
+ if (DiskIsValidSmartSelfTest(cmdInParameters->irDriveRegs.bSectorNumberReg)) {
+
+ controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
+ }
+ break;
+
+ case ENABLE_DISABLE_AUTO_OFFLINE:
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE;
+ break;
+ }
+ }
+
+ if (controlCode == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Invalid request.\n"));
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ length += max(sizeof(SENDCMDOUTPARAMS), sizeof(SENDCMDINPARAMS));
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(SRB_IO_CONTROL) + length,
+ DISK_TAG_SMART);
+
+ if (srbControl == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Unable to allocate memory.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // fill in srbControl fields
+ //
+
+ srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+ RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
+ srbControl->Timeout = fdoExtension->TimeOutValue;
+ srbControl->Length = length;
+ srbControl->ControlCode = controlCode;
+
+ //
+ // Point to the 'buffer' portion of the SRB_CONTROL
+ //
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ //
+ // Ensure correct target is set in the cmd parameters.
+ //
+
+ cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
+
+ //
+ // Copy the IOCTL parameters to the srb control buffer area.
+ //
+
+ if (cmdInParameters->irDriveRegs.bFeaturesReg == SMART_WRITE_LOG) {
+ RtlMoveMemory(buffer,
+ Irp->AssociatedIrp.SystemBuffer,
+ sizeof(SENDCMDINPARAMS) - 1 +
+ cmdInParameters->irDriveRegs.bSectorCountReg * SMART_LOG_SECTOR_SIZE);
+ } else {
+ RtlMoveMemory(buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
+ }
+
+ irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
+ commonExtension->LowerDeviceObject,
+ srbControl,
+ sizeof(SRB_IO_CONTROL) + length,
+ srbControl,
+ sizeof(SRB_IO_CONTROL) + length,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp2 == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "DiskIoctlSmartSendDriveCommand: Unable to allocate IRP.\n"));
+ FREE_POOL(srbControl);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, irp2);
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ //
+ // Copy the data received into the output buffer. Since the status buffer
+ // contains error information also, always perform this copy. IO will
+ // either pass this back to the app, or zero it, in case of error.
+ //
+
+ buffer = (PUCHAR)srbControl + srbControl->HeaderLength;
+
+ //
+ // Update the return buffer size based on the sub-command.
+ //
+
+ if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) {
+ length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
+ } else {
+ length = sizeof(SENDCMDOUTPARAMS) - 1;
+ }
+
+ RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, buffer, length);
+ Irp->IoStatus.Information = length;
+
+ FREE_POOL(srbControl);
+
+ return status;
+}
+
+
+VOID
+DiskEtwEnableCallback (
+ _In_ LPCGUID SourceId,
+ _In_ ULONG IsEnabled,
+ _In_ UCHAR Level,
+ _In_ ULONGLONG MatchAnyKeyword,
+ _In_ ULONGLONG MatchAllKeyword,
+ _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
+ _In_opt_ PVOID CallbackContext
+ )
+/*+++
+
+Routine Description:
+
+ This routine is the enable callback routine for ETW. It gets called when
+ tracing is enabled or disabled for our provider.
+
+Arguments:
+
+ As per the ETW callback.
+
+Return Value:
+
+ None.
+--*/
+
+{
+ //
+ // Initialize locals.
+ //
+ UNREFERENCED_PARAMETER(SourceId);
+ UNREFERENCED_PARAMETER(Level);
+ UNREFERENCED_PARAMETER(MatchAnyKeyword);
+ UNREFERENCED_PARAMETER(MatchAllKeyword);
+ UNREFERENCED_PARAMETER(FilterData);
+ UNREFERENCED_PARAMETER(CallbackContext);
+
+ //
+ // Set the ETW tracing enable state.
+ //
+ DiskETWEnabled = IsEnabled ? TRUE : FALSE;
+
+ return;
+}
+
+
diff --git a/storage/class/disk/src/disk.h b/storage/class/disk/src/disk.h
new file mode 100644
index 00000000..91427ba3
--- /dev/null
+++ b/storage/class/disk/src/disk.h
@@ -0,0 +1,1257 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "ntddk.h"
+#include "scsi.h"
+#include <wmidata.h>
+#include "classpnp.h"
+
+#include <wmistr.h>
+#include "ntstrsafe.h"
+
+//
+// Set component ID for DbgPrintEx calls
+//
+#ifndef DEBUG_COMP_ID
+#define DEBUG_COMP_ID DPFLTR_DISK_ID
+#endif
+
+//
+// Include header file and setup GUID for tracing
+//
+#include <storswtr.h>
+#define WPP_GUID_DISK (945186BF, 3DD6, 4f3f, 9C8E, 9EDD3FC9D558)
+#ifndef WPP_CONTROL_GUIDS
+#define WPP_CONTROL_GUIDS WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_DISK)
+#endif
+
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#define ExAllocatePool #NT_ASSERT(FALSE)
+#endif
+
+#define DISK_TAG_GENERAL ' DcS' // "ScD " - generic tag
+#define DISK_TAG_SMART 'aDcS' // "ScDa" - SMART allocations
+#define DISK_TAG_INFO_EXCEPTION 'ADcS' // "ScDA" - Info Exceptions
+#define DISK_TAG_DISABLE_CACHE 'CDcS' // "ScDC" - disable cache paths
+#define DISK_TAG_CCONTEXT 'cDcS' // "ScDc" - disk allocated completion context
+#define DISK_TAG_DISK_GEOM 'GDcS' // "ScDG" - disk geometry buffer
+#define DISK_TAG_UPDATE_GEOM 'gDcS' // "ScDg" - update disk geometry paths
+#define DISK_TAG_SENSE_INFO 'IDcS' // "ScDI" - sense info buffers
+#define DISK_TAG_PNP_ID 'iDcS' // "ScDp" - pnp ids
+#define DISK_TAG_MODE_DATA 'MDcS' // "ScDM" - mode data buffer
+#define DISK_CACHE_MBR_CHECK 'mDcS' // "ScDM" - mbr checksum code
+#define DISK_TAG_NAME 'NDcS' // "ScDN" - disk name code
+#define DISK_TAG_READ_CAP 'PDcS' // "ScDP" - read capacity buffer
+#define DISK_TAG_PART_LIST 'pDcS' // "ScDp" - disk partition lists
+#define DISK_TAG_SRB 'SDcS' // "ScDS" - srb allocation
+#define DISK_TAG_START 'sDcS' // "ScDs" - start device paths
+#define DISK_TAG_UPDATE_CAP 'UDcS' // "ScDU" - update capacity path
+#define DISK_TAG_WI_CONTEXT 'WDcS' // "ScDW" - work-item context
+
+
+#if defined(_X86_) || defined(_AMD64_)
+
+//
+// Disk device data
+//
+
+typedef enum _DISK_GEOMETRY_SOURCE {
+ DiskGeometryUnknown,
+ DiskGeometryFromBios,
+ DiskGeometryFromPort,
+ DiskGeometryFromNec98,
+ DiskGeometryGuessedFromBios,
+ DiskGeometryFromDefault,
+ DiskGeometryFromNT4
+} DISK_GEOMETRY_SOURCE, *PDISK_GEOMETRY_SOURCE;
+#endif
+
+//
+// Context for requests that can be combined and sent down
+//
+
+typedef struct _DISK_GROUP_CONTEXT
+{
+ //
+ // Queue of requests whose representative is currently outstanding at the port driver
+ //
+ LIST_ENTRY CurrList;
+
+ //
+ // The representative for the above queue
+ //
+ PIRP CurrIrp;
+
+ //
+ // Queue of requests whose representative is waiting to go down
+ //
+ LIST_ENTRY NextList;
+
+ //
+ // The representative for the above queue
+ //
+ PIRP NextIrp;
+
+ //
+ // The srb associated with this group
+ //
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+
+ union {
+ SCSI_REQUEST_BLOCK Srb;
+ STORAGE_REQUEST_BLOCK SrbEx;
+ UCHAR SrbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
+ } Srb;
+
+#else
+ SCSI_REQUEST_BLOCK Srb;
+#endif
+
+ //
+ // The spinlock that will synchronize access to this context
+ //
+ KSPIN_LOCK Spinlock;
+
+ //
+ // This event will allow for the requests to be sent down synchronously
+ //
+ KEVENT Event;
+
+
+#if DBG
+
+ //
+ // This counter maintains the number of requests currently tagged
+ // to the request that is waiting to go down
+ //
+ ULONG DbgTagCount;
+
+ //
+ // This counter maintains the number of requests that were avoided
+ //
+ ULONG DbgSavCount;
+
+ //
+ // This counter maintains the total number of times that we combined
+ // requests and the respective number of requests that were tagged
+ //
+ ULONG DbgRefCount[64];
+
+#endif
+
+} DISK_GROUP_CONTEXT, *PDISK_GROUP_CONTEXT;
+
+//
+// Write cache setting as defined by the user
+//
+typedef enum _DISK_USER_WRITE_CACHE_SETTING
+{
+ DiskWriteCacheDisable = 0,
+ DiskWriteCacheEnable = 1,
+ DiskWriteCacheDefault = -1
+
+} DISK_USER_WRITE_CACHE_SETTING, *PDISK_USER_WRITE_CACHE_SETTING;
+
+typedef struct _DISK_DATA {
+
+ //
+ // This field is the ordinal of a partition as it appears on a disk.
+ //
+
+ ULONG PartitionOrdinal;
+
+ //
+ // How has this disk been partitioned? Either EFI or MBR.
+ //
+
+ PARTITION_STYLE PartitionStyle;
+
+ union {
+
+ struct {
+
+ //
+ // Disk signature (from MBR)
+ //
+
+ ULONG Signature;
+
+ //
+ // MBR checksum
+ //
+
+ ULONG MbrCheckSum;
+
+ //
+ // Number of hidden sectors for BPB.
+ //
+
+ ULONG HiddenSectors;
+
+ //
+ // 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;
+
+ } Mbr;
+
+ struct {
+
+ //
+ // The DiskGUID field from the EFI partition header.
+ //
+
+ GUID DiskId;
+
+ //
+ // Partition type of this device object.
+ //
+
+ GUID PartitionType;
+
+ //
+ // Unique partition identifier for this partition.
+ //
+
+ GUID PartitionId;
+
+ //
+ // EFI partition attributes for this partition.
+ //
+
+ ULONG64 Attributes;
+
+ //
+ // EFI partition name of this partition.
+ //
+
+ WCHAR PartitionName[36];
+
+ } Efi;
+
+#pragma warning(suppress: 4201) //this is intended to be an unnamed union
+ };
+
+ struct {
+ //
+ // This flag is set when the well known name is created (through
+ // DiskCreateSymbolicLinks) and cleared when destroying it
+ // (by calling DiskDeleteSymbolicLinks).
+ //
+
+ unsigned int WellKnownNameCreated : 1;
+
+ //
+ // This flag is set when the PhysicalDriveN link is created (through
+ // DiskCreateSymbolicLinks) and is cleared when destroying it (through
+ // DiskDeleteSymbolicLinks)
+ //
+
+ unsigned int PhysicalDriveLinkCreated : 1;
+
+ } LinkStatus;
+
+ //
+ // ReadyStatus - STATUS_SUCCESS indicates that the drive is ready for
+ // use. Any error status is to be returned as an explaination for why
+ // a request is failed.
+ //
+ // This was done solely for the zero-length partition case of having no
+ // media in a removable disk drive. When that occurs, and a read is sent
+ // to the zero-length non-partition-zero PDO that was created, we had to
+ // be able to fail the request with a reasonable value. This may not have
+ // been the best way to do this, but it works.
+ //
+
+ NTSTATUS ReadyStatus;
+
+ //
+ // SCSI address used for SMART operations.
+ //
+
+ SCSI_ADDRESS ScsiAddress;
+
+ //
+ // What type of failure prediction mechanism is available
+ //
+
+ FAILURE_PREDICTION_METHOD FailurePredictionCapability;
+ BOOLEAN AllowFPPerfHit;
+
+ //
+ // Indicates that the SCSI Informational Exceptions mode page is supported.
+ // Note that this only indicates *support* and does not necessarily
+ // indicate that Informational Exception reporting via sense code is
+ // actually enabled.
+ //
+ BOOLEAN ScsiInfoExceptionsSupported;
+
+ //
+ // Indicates if failure prediction is actually enabled (via whatever)
+ // method is applicable as indicated by FailurePredictionCapability.
+ //
+ BOOLEAN FailurePredictionEnabled;
+
+#if defined(_X86_) || defined(_AMD64_)
+ //
+ // This flag indiciates that a non-default geometry for this drive has
+ // already been determined by the disk driver. This field is ignored
+ // for removable media drives.
+ //
+
+ DISK_GEOMETRY_SOURCE GeometrySource;
+
+ //
+ // If GeometryDetermined is TRUE this will contain the geometry which was
+ // reported by the firmware or by the BIOS. For removable media drives
+ // this will contain the last geometry used when media was present.
+ //
+
+ DISK_GEOMETRY RealGeometry;
+#endif
+
+ //
+ // This mutex prevents more than one IOCTL_DISK_VERIFY from being
+ // sent down to the disk. This greatly reduces the possibility of
+ // a Denial-of-Service attack
+ //
+
+ KMUTEX VerifyMutex;
+
+ //
+ // This allows for parallel flush requests to be combined into one so as to
+ // reduce the number of outstanding requests that are sent down to the disk
+ //
+
+ DISK_GROUP_CONTEXT FlushContext;
+
+ //
+ // The user-specified disk write cache setting
+ //
+
+ DISK_USER_WRITE_CACHE_SETTING WriteCacheOverride;
+
+
+} DISK_DATA, *PDISK_DATA;
+
+//
+// Define a general structure of identfing disk controllers with bad
+// hardware.
+//
+
+#define HackDisableTaggedQueuing (0x01)
+#define HackDisableSynchronousTransfers (0x02)
+#define HackDisableSpinDown (0x04)
+#define HackDisableWriteCache (0x08)
+#define HackCauseNotReportableHack (0x10)
+#define HackRequiresStartUnitCommand (0x20)
+
+
+#define DiskDeviceParameterSubkey L"Disk"
+#define DiskDeviceUserWriteCacheSetting L"UserWriteCacheSetting"
+#define DiskDeviceCacheIsPowerProtected L"CacheIsPowerProtected"
+
+
+#define FUNCTIONAL_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(DISK_DATA)
+
+#define MODE_DATA_SIZE 192
+#define VALUE_BUFFER_SIZE 2048
+#define SCSI_DISK_TIMEOUT 10
+#define PARTITION0_LIST_SIZE 4
+
+#define MAX_MEDIA_TYPES 4
+typedef struct _DISK_MEDIA_TYPES_LIST {
+ PCCHAR VendorId;
+ PCCHAR ProductId;
+ PCCHAR Revision;
+ const ULONG NumberOfTypes;
+ const ULONG NumberOfSides;
+ const STORAGE_MEDIA_TYPE MediaTypes[MAX_MEDIA_TYPES];
+} DISK_MEDIA_TYPES_LIST, *PDISK_MEDIA_TYPES_LIST;
+
+//
+// WMI reregistration structures used for reregister work item
+//
+typedef struct
+{
+ SINGLE_LIST_ENTRY Next;
+ PDEVICE_OBJECT DeviceObject;
+ PIRP Irp;
+} DISKREREGREQUEST, *PDISKREREGREQUEST;
+
+#define MAX_SECTORS_PER_VERIFY 0x100
+
+//
+// This is based off 100ns units
+//
+#define ONE_MILLI_SECOND ((ULONGLONG)10 * 1000)
+
+//
+// Context for the work-item
+//
+typedef struct _DISK_VERIFY_WORKITEM_CONTEXT
+{
+ PIRP Irp;
+ PSCSI_REQUEST_BLOCK Srb;
+ PIO_WORKITEM WorkItem;
+
+} DISK_VERIFY_WORKITEM_CONTEXT, *PDISK_VERIFY_WORKITEM_CONTEXT;
+
+//
+// Poll for Failure Prediction every hour
+//
+#define DISK_DEFAULT_FAILURE_POLLING_PERIOD 1 * 60 * 60
+
+#define CHECK_IRQL() \
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { \
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); \
+ return STATUS_INVALID_LEVEL; \
+ }
+
+//
+// Static global lookup tables.
+//
+
+extern CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[];
+extern const DISK_MEDIA_TYPES_LIST DiskMediaTypes[];
+extern const DISK_MEDIA_TYPES_LIST DiskMediaTypesExclude[];
+
+//
+// Macros
+//
+
+//
+// Routine prototypes.
+//
+
+
+DRIVER_INITIALIZE DriverEntry;
+
+VOID
+DiskUnload(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+NTSTATUS
+DiskAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT Pdo
+ );
+
+NTSTATUS
+DiskInitFdo(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+DiskStartFdo(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+DiskStopDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ );
+
+NTSTATUS
+DiskRemoveDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ );
+
+NTSTATUS
+DiskReadWriteVerification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DiskDeviceControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+DiskFdoProcessError(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+NTSTATUS
+DiskShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DiskGetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ );
+
+NTSTATUS
+DiskSetCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo
+ );
+
+VOID
+DiskLogCacheInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PDISK_CACHE_INFORMATION CacheInfo,
+ IN NTSTATUS Status
+ );
+
+NTSTATUS
+DiskIoctlGetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSetCacheSetting(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+IO_WORKITEM_ROUTINE DisableWriteCache;
+
+IO_WORKITEM_ROUTINE DiskIoctlVerifyThread;
+
+VOID
+DiskFlushDispatch(
+ IN PDEVICE_OBJECT Fdo,
+ IN PDISK_GROUP_CONTEXT FlushContext
+ );
+
+IO_COMPLETION_ROUTINE DiskFlushComplete;
+
+
+NTSTATUS
+DiskModeSelect(
+ IN PDEVICE_OBJECT DeviceObject,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ IN ULONG Length,
+ IN BOOLEAN SavePage
+ );
+
+//
+// We need to validate that the self test subcommand is valid and
+// appropriate. Right now we allow subcommands 0, 1 and 2 which are non
+// captive mode tests. Once we figure out a way to know if it is safe to
+// run a captive test then we can allow captive mode tests. Also if the
+// atapi 5 spec is ever updated to denote that bit 7 is the captive
+// mode bit, we can allow any request that does not have bit 7 set. Until
+// that is done we want to be sure
+//
+#define DiskIsValidSmartSelfTest(Subcommand) \
+ ( ((Subcommand) == SMART_OFFLINE_ROUTINE_OFFLINE) || \
+ ((Subcommand) == SMART_SHORT_SELFTEST_OFFLINE) || \
+ ((Subcommand) == SMART_EXTENDED_SELFTEST_OFFLINE) )
+
+
+NTSTATUS
+DiskPerformSmartCommand(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG SrbControlCode,
+ IN UCHAR Command,
+ IN UCHAR Feature,
+ IN UCHAR SectorCount,
+ IN UCHAR SectorNumber,
+ IN OUT PSRB_IO_CONTROL SrbControl,
+ OUT PULONG BufferSize
+ );
+
+NTSTATUS
+DiskGetInfoExceptionInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PMODE_INFO_EXCEPTIONS ReturnPageData
+ );
+
+NTSTATUS
+DiskSetInfoExceptionInformation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMODE_INFO_EXCEPTIONS PageData
+ );
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+NTSTATUS
+DiskGetModePage(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl,
+ _In_ PMODE_PARAMETER_HEADER ModeData,
+ _Inout_ PULONG ModeDataSize,
+ _Out_ PVOID* PageData
+ );
+
+NTSTATUS
+DiskEnableInfoExceptions(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN Enable
+ );
+#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+NTSTATUS
+DiskDetectFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PFAILURE_PREDICTION_METHOD FailurePredictCapability,
+ BOOLEAN ScsiAddressAvailable
+ );
+
+NTSTATUS
+DiskCreateFdo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT LowerDeviceObject,
+ IN PULONG DeviceCount,
+ IN BOOLEAN DasdAccessOnly
+ );
+
+VOID
+DiskSetSpecialHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ );
+
+VOID
+ResetBus(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+DiskGenerateDeviceName(
+ IN ULONG DeviceNumber,
+ OUT PCCHAR *RawName
+ );
+
+VOID
+DiskCreateSymbolicLinks(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+DiskDeleteSymbolicLinks(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+
+NTSTATUS
+DiskFdoQueryWmiRegInfo(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName
+ );
+
+NTSTATUS
+DiskFdoQueryWmiRegInfoEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName,
+ OUT PUNICODE_STRING MofName
+ );
+
+NTSTATUS
+DiskFdoQueryWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferAvail,
+ OUT PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskFdoSetWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskFdoSetWmiDataItem(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG DataItemId,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskFdoExecuteWmiMethod(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG MethodId,
+ IN ULONG InBufferSize,
+ IN ULONG OutBufferSize,
+ IN PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskWmiFunctionControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN CLASSENABLEDISABLEFUNCTION Function,
+ IN BOOLEAN Enable
+ );
+
+NTSTATUS
+DiskReadFailurePredictStatus(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus
+ );
+
+NTSTATUS
+DiskReadFailurePredictData(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData
+ );
+
+NTSTATUS
+DiskEnableDisableFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable
+ );
+
+NTSTATUS
+DiskEnableDisableFailurePredictPolling(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable,
+ ULONG PollTimeInSeconds
+ );
+
+NTSTATUS DiskInitializeReregistration(
+ VOID
+ );
+
+extern GUIDREGINFO DiskWmiFdoGuidList[];
+
+#if defined(_X86_) || defined(_AMD64_)
+NTSTATUS
+DiskReadDriveCapacity(
+ IN PDEVICE_OBJECT Fdo
+ );
+#else
+#define DiskReadDriveCapacity(Fdo) ClassReadDriveCapacity(Fdo)
+#endif
+
+
+#if defined(_X86_) || defined(_AMD64_)
+
+NTSTATUS
+DiskSaveDetectInfo(
+ PDRIVER_OBJECT DriverObject
+ );
+
+VOID
+DiskCleanupDetectInfo(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+VOID
+DiskDriverReinitialization (
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ );
+
+#endif
+
+#if defined(_X86_) || defined(_AMD64_)
+NTSTATUS
+DiskGetDetectInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PDISK_DETECTION_INFO DetectInfo
+ );
+
+NTSTATUS
+DiskReadSignature(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+BOOLEAN
+DiskIsNT4Geometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+#else
+#define DiskGetDetectInfo(FdoExtension, DetectInfo) (STATUS_UNSUCCESSFUL)
+#endif
+
+
+#define DiskHashGuid(Guid) (((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0])
+
+
+NTSTATUS
+DiskDetermineMediaTypes(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN UCHAR MediumType,
+ IN UCHAR DensityCode,
+ IN BOOLEAN MediaPresent,
+ IN BOOLEAN IsWritable
+ );
+
+NTSTATUS
+DiskIoctlGetLengthInfo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlGetDriveGeometry(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlGetDriveGeometryEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlGetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSetCacheInformation(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlGetMediaTypesEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlPredictFailure(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlEnableFailurePrediction(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlReassignBlocks(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlReassignBlocksEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlIsWritable(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSetVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlClearVerify(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlUpdateDriveSize(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlGetVolumeDiskExtents(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSmartGetVersion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSmartReceiveDriveData(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+NTSTATUS
+DiskIoctlSmartSendDriveCommand(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OUT PIRP Irp
+ );
+
+__inline
+PCDB
+GetSrbScsiData(
+ _In_ PSTORAGE_REQUEST_BLOCK SrbEx,
+ _In_opt_ PUCHAR CdbLength8,
+ _In_opt_ PULONG CdbLength32,
+ _In_opt_ PUCHAR ScsiStatus,
+ _In_opt_ PVOID *SenseInfoBuffer,
+ _In_opt_ PUCHAR SenseInfoBufferLength
+ )
+/*++
+
+Routine Description:
+
+ Helper function to retrieve SCSI related fields from an extended SRB. If SRB is
+ not a SRB_FUNCTION_EXECUTE_SCSI or not an extended SRB, default values will be returned.
+
+Arguments:
+
+ SrbEx - Pointer to extended SRB.
+
+ CdbLength8 - Pointer to buffer to hold CdbLength field value for
+ SRBEX_DATA_SCSI_CDB16 or SRBEX_DATA_SCSI_CDB32
+
+ CdbLength32 - Pointer to buffer to hold CdbLength field value for
+ SRBEX_DATA_SCSI_CDB_VAR
+
+ ScsiStatus - Pointer to buffer to hold ScsiStatus field value.
+
+ SenseInfoBuffer - Pointer to buffer to hold SenseInfoBuffer value.
+
+ SenseInfoBufferLength - Pointer to buffer to hold SenseInfoBufferLength value.
+
+Return Value:
+
+ Pointer to Cdb field or NULL if SRB is not a SRB_FUNCTION_EXECUTE_SCSI.
+
+--*/
+{
+ PCDB Cdb = NULL;
+ ULONG i;
+ PSRBEX_DATA SrbExData = NULL;
+ BOOLEAN FoundEntry = FALSE;
+
+ if ((SrbEx->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ (SrbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI)) {
+ NT_ASSERT(SrbEx->NumSrbExData > 0);
+
+ for (i = 0; i < SrbEx->NumSrbExData; i++) {
+
+ // Skip any invalid offsets
+ if ((SrbEx->SrbExDataOffset[i] < sizeof(STORAGE_REQUEST_BLOCK)) ||
+ (SrbEx->SrbExDataOffset[i] > SrbEx->SrbLength)){
+ // Catch any invalid offsets
+ NT_ASSERT(FALSE);
+ continue;
+ }
+
+ SrbExData = (PSRBEX_DATA)((PUCHAR)SrbEx + SrbEx->SrbExDataOffset[i]);
+
+ switch (SrbExData->Type) {
+
+ case SrbExDataTypeScsiCdb16:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB16) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ if (CdbLength8) {
+ *CdbLength8 = ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->CdbLength;
+ }
+
+ if (((PSRBEX_DATA_SCSI_CDB16) SrbExData)->CdbLength > 0) {
+ Cdb = (PCDB)((PSRBEX_DATA_SCSI_CDB16) SrbExData)->Cdb;
+ }
+
+ if (ScsiStatus) {
+ *ScsiStatus =
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->ScsiStatus;
+ }
+
+ if (SenseInfoBuffer) {
+ *SenseInfoBuffer =
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->SenseInfoBuffer;
+ }
+
+ if (SenseInfoBufferLength) {
+ *SenseInfoBufferLength =
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->SenseInfoBufferLength;
+ }
+
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+
+ case SrbExDataTypeScsiCdb32:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB32) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ if (CdbLength8) {
+ *CdbLength8 = ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->CdbLength;
+ }
+
+ if (((PSRBEX_DATA_SCSI_CDB32) SrbExData)->CdbLength > 0) {
+ Cdb = (PCDB)((PSRBEX_DATA_SCSI_CDB32) SrbExData)->Cdb;
+ }
+
+ if (ScsiStatus) {
+ *ScsiStatus =
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->ScsiStatus;
+ }
+
+ if (SenseInfoBuffer) {
+ *SenseInfoBuffer =
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->SenseInfoBuffer;
+ }
+
+ if (SenseInfoBufferLength) {
+ *SenseInfoBufferLength =
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->SenseInfoBufferLength;
+ }
+
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+
+ case SrbExDataTypeScsiCdbVar:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB_VAR) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ if (CdbLength32) {
+ *CdbLength32 = ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->CdbLength;
+ }
+
+ if (((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->CdbLength > 0) {
+ Cdb = (PCDB)((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->Cdb;
+ }
+
+ if (ScsiStatus) {
+ *ScsiStatus =
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->ScsiStatus;
+ }
+
+ if (SenseInfoBuffer) {
+ *SenseInfoBuffer =
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->SenseInfoBuffer;
+ }
+
+ if (SenseInfoBufferLength) {
+ *SenseInfoBufferLength =
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->SenseInfoBufferLength;
+ }
+
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+ }
+
+ if (FoundEntry) {
+ break;
+ }
+ }
+
+ } else {
+
+ if (CdbLength8) {
+ *CdbLength8 = 0;
+ }
+
+ if (CdbLength32) {
+ *CdbLength32 = 0;
+ }
+
+ if (ScsiStatus) {
+ *ScsiStatus = 0;
+ }
+
+ if (SenseInfoBuffer) {
+ *SenseInfoBuffer = NULL;
+ }
+
+ if (SenseInfoBufferLength) {
+ *SenseInfoBufferLength = 0;
+ }
+ }
+
+ return Cdb;
+}
+
+__inline
+VOID
+SetSrbScsiData(
+ _In_ PSTORAGE_REQUEST_BLOCK SrbEx,
+ _In_ UCHAR CdbLength8,
+ _In_ ULONG CdbLength32,
+ _In_ UCHAR ScsiStatus,
+ _In_opt_ PVOID SenseInfoBuffer,
+ _In_ UCHAR SenseInfoBufferLength
+ )
+/*++
+
+Routine Description:
+
+ Helper function to set SCSI related fields from an extended SRB. If SRB is
+ not a SRB_FUNCTION_EXECUTE_SCSI or not an extended SRB, no modifications will
+ be made
+
+Arguments:
+
+ SrbEx - Pointer to extended SRB.
+
+ CdbLength8 - CdbLength field value for SRBEX_DATA_SCSI_CDB16
+ or SRBEX_DATA_SCSI_CDB32
+
+ CdbLength32 - CdbLength field value for SRBEX_DATA_SCSI_CDB_VAR
+
+ ScsiStatus - ScsiStatus field value.
+
+ SenseInfoBuffer - SenseInfoBuffer value.
+
+ SenseInfoBufferLength - SenseInfoBufferLength value.
+
+Return Value:
+
+ None
+
+--*/
+{
+ ULONG i;
+ PSRBEX_DATA SrbExData = NULL;
+ BOOLEAN FoundEntry = FALSE;
+
+ if ((SrbEx->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ (SrbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI)) {
+ NT_ASSERT(SrbEx->NumSrbExData > 0);
+
+ for (i = 0; i < SrbEx->NumSrbExData; i++) {
+
+ // Skip any invalid offsets
+ if ((SrbEx->SrbExDataOffset[i] < sizeof(STORAGE_REQUEST_BLOCK)) ||
+ (SrbEx->SrbExDataOffset[i] > SrbEx->SrbLength)){
+ // Catch any invalid offsets
+ NT_ASSERT(FALSE);
+ continue;
+ }
+
+ SrbExData = (PSRBEX_DATA)((PUCHAR)SrbEx + SrbEx->SrbExDataOffset[i]);
+
+ switch (SrbExData->Type) {
+
+ case SrbExDataTypeScsiCdb16:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB16) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->CdbLength = CdbLength8;
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->ScsiStatus = ScsiStatus;
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->SenseInfoBuffer = SenseInfoBuffer;
+ ((PSRBEX_DATA_SCSI_CDB16) SrbExData)->SenseInfoBufferLength = SenseInfoBufferLength;
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+
+ case SrbExDataTypeScsiCdb32:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB32) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->CdbLength = CdbLength8;
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->ScsiStatus = ScsiStatus;
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->SenseInfoBuffer = SenseInfoBuffer;
+ ((PSRBEX_DATA_SCSI_CDB32) SrbExData)->SenseInfoBufferLength = SenseInfoBufferLength;
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+
+ case SrbExDataTypeScsiCdbVar:
+ if (SrbEx->SrbExDataOffset[i] + sizeof(SRBEX_DATA_SCSI_CDB_VAR) <= SrbEx->SrbLength) {
+ FoundEntry = TRUE;
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->CdbLength = CdbLength32;
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->ScsiStatus = ScsiStatus;
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->SenseInfoBuffer = SenseInfoBuffer;
+ ((PSRBEX_DATA_SCSI_CDB_VAR) SrbExData)->SenseInfoBufferLength = SenseInfoBufferLength;
+ } else {
+ // Catch invalid offset
+ NT_ASSERT(FALSE);
+ }
+ break;
+ }
+
+ if (FoundEntry) {
+ break;
+ }
+ }
+
+ }
+
+ return;
+}
+
diff --git a/storage/class/disk/src/disk.htm b/storage/class/disk/src/disk.htm
new file mode 100644
index 00000000..bb77f1fd
--- /dev/null
+++ b/storage/class/disk/src/disk.htm
@@ -0,0 +1,42 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
+<META NAME="Generator" CONTENT="Microsoft Word 97">
+<TITLE>Disk</TITLE>
+<META NAME="Template" CONTENT="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
+</HEAD>
+<BODY TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff" leftmargin="8">
+<FONT FACE="Verdana"><H2><A NAME="disk">Disk</A> </H2>
+
+<H3>Summary</H3></FONT><FONT FACE="Verdana" SIZE=2><P>
+The Disk sample is used with Classpnp.sys as disk driver. The sample supports Plug and Play, Power Management, WMI, and failure prediction (S.M.A.R.T.), and it is 64-bit compliant.
+
+<H3>Building the Sample</H3></FONT><FONT FACE="Verdana" SIZE=2><P>
+To build the sample, run the <b>build</B> command. Once built, this sample will create one binary: Disk.sys.
+
+<H3>CODE TOUR</H3>
+<H4>File Manifest</H4>
+</FONT><U><PRE>File&#9;&#9; Description
+</U>
+Data.c&#9; List of workarounds for some drives (no code)
+Disk.c&#9; Main code base
+Disk.h&#9; Private header files
+Disk.rc&#9; Resource file
+Diskwmi.c&#9; S.M.A.R.T. WMI implementation
+Drivesup.c&#9; No longer used
+Drivesup.h&#9; No longer used
+Enum.c&#9; Enumeration routines for disk drives
+Makefile&#9; Makefile
+Pnp.c&#9; Start/add/pnp code
+Sources&#9; Sources
+
+
+</FONT><P ALIGN="CENTER"><A HREF="#top"><FONT FACE="Verdana" SIZE=2>Top of page</FONT></A><FONT FACE="Verdana" SIZE=2> </P></FONT>
+<TABLE CELLSPACING=0 BORDER=0 WIDTH=624>
+<TR><TD VALIGN="MIDDLE" BGCOLOR="#00ffff" HEIGHT=2>
+<P></TD>
+</TR>
+</TABLE>
+
+<FONT FACE="MS Sans Serif" SIZE=1><P>&copy; 1999 Microsoft Corporation</FONT><FONT FACE="Verdana" SIZE=2> </P></FONT></BODY>
+</HTML>
diff --git a/storage/class/disk/src/disk.rc b/storage/class/disk/src/disk.rc
new file mode 100644
index 00000000..b697f15b
--- /dev/null
+++ b/storage/class/disk/src/disk.rc
@@ -0,0 +1,23 @@
+/* ------------------------------------------------------------------------- *
+ * *
+ * Copyright (c) Microsoft Corporation *
+ * *
+ * Module Name : disk.rc *
+ * *
+ * Abstract : Resource script for disk.sys *
+ * *
+ * ------------------------------------------------------------------------- */
+
+#include <verrsrc.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "PnP Disk Driver"
+#define VER_INTERNALNAME_STR "disk.sys"
+#define VER_ORIGINALFILENAME_STR "disk.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
+
diff --git a/storage/class/disk/src/disk.vcxproj b/storage/class/disk/src/disk.vcxproj
new file mode 100644
index 00000000..7d749d1c
--- /dev/null
+++ b/storage/class/disk/src/disk.vcxproj
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{627F8680-3389-4050-A0E1-9EA14F615498}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <SupportsPackaging>false</SupportsPackaging>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{0AA04C84-504B-44EC-90C4-BDC48593BE4F}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>WDM</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="data.c; disk.c; diskwmi.c; geometry.c; pnp.c">
+ <WppEnabled Condition="'$(UseDebugLibraries)'=='false'">true</WppEnabled>
+ <WppKernelMode Condition="'$(UseDebugLibraries)'=='false'">true</WppKernelMode>
+ <WppTraceFunction Condition="'$(UseDebugLibraries)'=='false'">TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction>
+ </ClCompile>
+ <OtherWpp Include="disk.rc">
+ <WppEnabled Condition="'$(UseDebugLibraries)'=='false'">true</WppEnabled>
+ <WppKernelMode Condition="'$(UseDebugLibraries)'=='false'">true</WppKernelMode>
+ <WppTraceFunction Condition="'$(UseDebugLibraries)'=='false'">TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>disk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>disk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>disk</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>disk</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\classpnp.lib</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\classpnp.lib</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\classpnp.lib</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\classpnp.lib</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc;..\..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="disk.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/storage/class/disk/src/disk.vcxproj.Filters b/storage/class/disk/src/disk.vcxproj.Filters
new file mode 100644
index 00000000..7c1cdf7d
--- /dev/null
+++ b/storage/class/disk/src/disk.vcxproj.Filters
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{F46D90F7-048E-494E-B795-E7FE53BA14F3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{99D7E70D-CDF2-4F0C-A0DC-E296CCFD9C4D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{3C108ECD-99BB-4DF0-9937-B9E5541E1831}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{64085F1A-1949-4F74-A70E-5A1FB0584FD8}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="data.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="disk.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="diskwmi.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="geometry.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="pnp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="disk.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/storage/class/disk/src/diskdev.inf b/storage/class/disk/src/diskdev.inf
new file mode 100644
index 00000000..78bea6a4
--- /dev/null
+++ b/storage/class/disk/src/diskdev.inf
@@ -0,0 +1,113 @@
+; disk.inf
+;
+; Installation inf for the Disk drive adapter.
+;
+; SAMPLE INF File for Class Driver
+; FOR DDK - Driver Development Kit
+;
+; (c) Copyright 1999 Microsoft Corp.
+;
+
+[Version]
+Signature="$Windows NT$"
+Provider=%MS%
+ClassGUID={4d36e967-e325-11ce-bfc1-08002be10318}
+Class=DiskDrive
+DriverVer=08/27/1999,5.2.1425.0
+;CatalogFile=your_cat_here.cat ; Supply your own catalog file here.
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+;
+; Driver information
+;
+
+[Manufacturer]
+%MS% = MS.Mfg, NTx86, NTia64, NTamd64
+
+[MS.Mfg.NTx86]
+%MS.DeviceDesc0% = disk, GenDisk
+%MS.DeviceDesc1% = disk, GenOptical
+
+[MS.Mfg.NTia64]
+%MS.DeviceDesc0% = disk, GenDisk
+%MS.DeviceDesc1% = disk, GenOptical
+
+[MS.Mfg.NTamd64]
+%MS.DeviceDesc0% = disk, GenDisk
+%MS.DeviceDesc1% = disk, GenOptical
+
+
+;
+; General installation section
+;
+
+[disk.NT]
+CopyFiles=disk.CopyFiles
+
+;
+; File sections
+;
+
+[disk.CopyFiles]
+disk.sys
+
+
+;
+; Service Installation
+;
+
+[disk.NT.Services]
+AddService = disk, 0x00000002 , disk_Service_Inst
+
+[disk_Service_Inst]
+DisplayName = %disk.SvcDesc%
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 0 ; SERVICE_SYSTEM_START
+ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+LoadOrderGroup = SCSI Class
+ServiceBinary = %12%\disk.sys
+
+
+;
+; Source file information
+;
+
+[SourceDisksNames.x86]
+1 = %DiskId1%,,,\i386
+
+[SourceDisksNames.ia64]
+1 = %DiskId1%,,,\ia64
+
+[SourceDisksNames.amd64]
+1 = %DiskId1%,,,\amd64
+
+[SourceDisksFiles]
+; Files for disk Microsoft Corp. Installation Disk #1 (DiskDrive)
+disk.sys = 1,,
+
+
+[Strings]
+
+;
+; Non-Localizable Strings
+;
+
+REG_SZ = 0x00000000
+REG_MULTI_SZ = 0x00010000
+REG_EXPAND_SZ = 0x00020000
+REG_BINARY = 0x00000001
+REG_DWORD = 0x00010001
+SERVICEROOT = "System\CurrentControlSet\Services"
+
+;
+; Localizable Strings
+;
+
+MS.DeviceDesc0 = "Disk drive"
+MS.DeviceDesc1 = "Optical disk drive"
+DiskId1 = "Microsoft Corp. Installation Disk #1 (DiskDrive)"
+MS = "Microsoft Corp."
+disk.SvcDesc="Disk Drive"
+
diff --git a/storage/class/disk/src/diskwmi.c b/storage/class/disk/src/diskwmi.c
new file mode 100644
index 00000000..c1a0945e
--- /dev/null
+++ b/storage/class/disk/src/diskwmi.c
@@ -0,0 +1,3501 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ diskwmi.c
+
+Abstract:
+
+ SCSI disk class driver - WMI support routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "disk.h"
+
+#ifdef DEBUG_USE_WPP
+#include "diskwmi.tmh"
+#endif
+
+NTSTATUS
+DiskSendFailurePredictIoctl(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_PREDICT_FAILURE checkFailure
+ );
+
+NTSTATUS
+DiskGetIdentifyInfo(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PBOOLEAN SupportSmart
+ );
+
+NTSTATUS
+DiskDetectFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PFAILURE_PREDICTION_METHOD FailurePredictCapability,
+ BOOLEAN ScsiAddressAvailable
+ );
+
+NTSTATUS
+DiskReadFailurePredictThresholds(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
+ );
+
+NTSTATUS
+DiskReadSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ OUT PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskWriteSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ IN PUCHAR Buffer
+ );
+
+IO_WORKITEM_ROUTINE DiskReregWorker;
+
+IO_COMPLETION_ROUTINE DiskInfoExceptionComplete;
+
+//
+// WMI reregistration globals
+//
+// Since it will take too long to do a mode sense on some drive, we
+// need a good way to effect the mode sense for the info exceptions
+// mode page so that we can determine if SMART is supported and enabled
+// for the drive. So the strategy is to do an asynchronous mode sense
+// when the device starts and then look at the info exceptions mode
+// page within the completion routine. Now within the completion
+// routine we cannot call IoWMIRegistrationControl since we are at DPC
+// level, so we create a stack of device objects that will be processed
+// by a single work item that is fired off only when the stack
+// transitions from empty to non empty.
+//
+SINGLE_LIST_ENTRY DiskReregHead;
+KSPIN_LOCK DiskReregSpinlock;
+LONG DiskReregWorkItems;
+
+GUIDREGINFO DiskWmiFdoGuidList[] =
+{
+ {
+ WMI_DISK_GEOMETRY_GUID,
+ 1,
+ 0
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_STATUS_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_DATA_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_FUNCTION_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID,
+ 1,
+ WMIREG_FLAG_EVENT_ONLY_GUID
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_THRESHOLDS_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_SCSI_INFO_EXCEPTIONS_GUID,
+ 1,
+ 0
+ }
+};
+
+
+GUID DiskPredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
+
+#define DiskGeometryGuid 0
+#define SmartStatusGuid 1
+#define SmartDataGuid 2
+#define SmartPerformFunction 3
+ #define AllowDisallowPerformanceHit 1
+ #define EnableDisableHardwareFailurePrediction 2
+ #define EnableDisableFailurePredictionPolling 3
+ #define GetFailurePredictionCapability 4
+ #define EnableOfflineDiags 5
+
+#define SmartEventGuid 4
+#define SmartThresholdsGuid 5
+#define ScsiInfoExceptionsGuid 6
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DiskWmiFunctionControl)
+#pragma alloc_text(PAGE, DiskFdoQueryWmiRegInfo)
+#pragma alloc_text(PAGE, DiskFdoQueryWmiDataBlock)
+#pragma alloc_text(PAGE, DiskFdoSetWmiDataBlock)
+#pragma alloc_text(PAGE, DiskFdoSetWmiDataItem)
+#pragma alloc_text(PAGE, DiskFdoExecuteWmiMethod)
+
+#pragma alloc_text(PAGE, DiskDetectFailurePrediction)
+#pragma alloc_text(PAGE, DiskEnableDisableFailurePrediction)
+#pragma alloc_text(PAGE, DiskEnableDisableFailurePredictPolling)
+#pragma alloc_text(PAGE, DiskReadFailurePredictStatus)
+#pragma alloc_text(PAGE, DiskReadFailurePredictData)
+#pragma alloc_text(PAGE, DiskReadFailurePredictThresholds)
+#pragma alloc_text(PAGE, DiskGetIdentifyInfo)
+#pragma alloc_text(PAGE, DiskReadSmartLog)
+#pragma alloc_text(PAGE, DiskWriteSmartLog)
+#pragma alloc_text(PAGE, DiskPerformSmartCommand)
+#pragma alloc_text(PAGE, DiskSendFailurePredictIoctl)
+#pragma alloc_text(PAGE, DiskReregWorker)
+#pragma alloc_text(PAGE, DiskInitializeReregistration)
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+#pragma alloc_text(PAGE, DiskGetModePage)
+#pragma alloc_text(PAGE, DiskEnableInfoExceptions)
+#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+#endif
+
+
+//
+// Note:
+// Some port drivers assume that the SENDCMDINPARAMS structure will always be atleast
+// sizeof(SENDCMDINPARAMS). So do not adjust for the [pBuffer] if it isn't being used
+//
+
+//
+// SMART/IDE specific routines
+//
+
+//
+// Read SMART data attributes.
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE ]
+// Attribute data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskReadSmartData(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS, \
+ SMART_CMD, \
+ READ_ATTRIBUTES, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read SMART data thresholds.
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE ]
+// Attribute data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskReadSmartThresholds(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS, \
+ SMART_CMD, \
+ READ_THRESHOLDS, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read SMART status
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) ]
+// Failure predicted if SendCmdOutParams->bBuffer[3] == 0xf4 and SendCmdOutParams->bBuffer[4] == 0x2c
+//
+#define DiskReadSmartStatus(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_RETURN_STATUS, \
+ SMART_CMD, \
+ RETURN_SMART_STATUS, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read disks IDENTIFY data
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE ]
+// Identify data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskGetIdentifyData(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_IDENTIFY, \
+ ID_CMD, \
+ 0, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Enable SMART
+//
+_inline NTSTATUS
+DiskEnableSmart(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_SMART,
+ SMART_CMD,
+ ENABLE_SMART,
+ 0,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Disable SMART
+//
+_inline NTSTATUS
+DiskDisableSmart(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_DISABLE_SMART,
+ SMART_CMD,
+ DISABLE_SMART,
+ 0,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Enable Attribute Autosave
+//
+_inline NTSTATUS
+DiskEnableSmartAttributeAutosave(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
+ SMART_CMD,
+ ENABLE_DISABLE_AUTOSAVE,
+ 0xf1,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Disable Attribute Autosave
+//
+_inline NTSTATUS
+DiskDisableSmartAttributeAutosave(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
+ SMART_CMD,
+ ENABLE_DISABLE_AUTOSAVE,
+ 0x00,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Initialize execution of SMART online diagnostics
+//
+_inline NTSTATUS
+DiskExecuteSmartDiagnostics(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ UCHAR Subcommand
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS,
+ SMART_CMD,
+ EXECUTE_OFFLINE_DIAGS,
+ 0,
+ Subcommand,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+NTSTATUS
+DiskReadSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ OUT PUCHAR Buffer
+ )
+{
+ PSRB_IO_CONTROL srbControl;
+ NTSTATUS status;
+ PSENDCMDOUTPARAMS sendCmdOutParams;
+ ULONG logSize, bufferSize;
+
+ PAGED_CODE();
+
+ logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
+ bufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + logSize );
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferSize,
+ DISK_TAG_SMART);
+
+ if (srbControl != NULL)
+ {
+ status = DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_READ_SMART_LOG,
+ SMART_CMD,
+ SMART_READ_LOG,
+ SectorCount,
+ LogAddress,
+ srbControl,
+ &bufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ sendCmdOutParams = (PSENDCMDOUTPARAMS)((PUCHAR)srbControl +
+ sizeof(SRB_IO_CONTROL));
+ RtlCopyMemory(Buffer,
+ &sendCmdOutParams->bBuffer[0],
+ logSize);
+ }
+
+ FREE_POOL(srbControl);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ return(status);
+}
+
+
+NTSTATUS
+DiskWriteSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ IN PUCHAR Buffer
+ )
+{
+ PSRB_IO_CONTROL srbControl;
+ NTSTATUS status;
+ PSENDCMDINPARAMS sendCmdInParams;
+ ULONG logSize, bufferSize;
+
+ PAGED_CODE();
+
+ logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
+ bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 +
+ logSize;
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferSize,
+ DISK_TAG_SMART);
+
+ if (srbControl != NULL)
+ {
+ sendCmdInParams = (PSENDCMDINPARAMS)((PUCHAR)srbControl +
+ sizeof(SRB_IO_CONTROL));
+ RtlCopyMemory(&sendCmdInParams->bBuffer[0],
+ Buffer,
+ logSize);
+ status = DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG,
+ SMART_CMD,
+ SMART_WRITE_LOG,
+ SectorCount,
+ LogAddress,
+ srbControl,
+ &bufferSize);
+
+ FREE_POOL(srbControl);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ return(status);
+}
+
+
+NTSTATUS
+DiskPerformSmartCommand(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG SrbControlCode,
+ IN UCHAR Command,
+ IN UCHAR Feature,
+ IN UCHAR SectorCount,
+ IN UCHAR SectorNumber,
+ IN OUT PSRB_IO_CONTROL SrbControl,
+ OUT PULONG BufferSize
+ )
+/*++
+
+Routine Description:
+
+ This routine will perform some SMART command
+
+Arguments:
+
+ FdoExtension is the FDO device extension
+
+ SrbControlCode is the SRB control code to use for the request
+
+ Command is the SMART command to be executed. It may be SMART_CMD or
+ ID_CMD.
+
+ Feature is the value to place in the IDE feature register.
+
+ SectorCount is the value to place in the IDE SectorCount register
+
+ SrbControl is the buffer used to build the SRB_IO_CONTROL and pass
+ any input parameters. It also returns the output parameters.
+
+ *BufferSize on entry has total size of SrbControl and on return has
+ the size used in SrbControl.
+
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PUCHAR buffer;
+ PSENDCMDINPARAMS cmdInParameters;
+ NTSTATUS status;
+ ULONG availableBufferSize;
+ KEVENT event;
+ PIRP irp;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ SCSI_REQUEST_BLOCK srb = {0};
+ LARGE_INTEGER startingOffset;
+ ULONG length;
+ PIO_STACK_LOCATION irpStack;
+ UCHAR srbExBuffer[CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+
+ PAGED_CODE();
+
+ //
+ // Point to the 'buffer' portion of the SRB_CONTROL and compute how
+ // much room we have left in the srb control. Abort if the buffer
+ // isn't at least the size of SRB_IO_CONTROL.
+ //
+
+ buffer = (PUCHAR)SrbControl + sizeof(SRB_IO_CONTROL);
+
+ cmdInParameters = (PSENDCMDINPARAMS)buffer;
+
+ if (*BufferSize >= sizeof(SRB_IO_CONTROL)) {
+ availableBufferSize = *BufferSize - sizeof(SRB_IO_CONTROL);
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+#if DBG
+
+ //
+ // Ensure control codes and buffer lengths passed are correct
+ //
+ {
+ ULONG controlCode = 0;
+ ULONG lengthNeeded = sizeof(SENDCMDINPARAMS);
+
+ if (Command == SMART_CMD)
+ {
+ switch (Feature)
+ {
+ case ENABLE_SMART:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
+ break;
+ }
+
+ case DISABLE_SMART:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
+ break;
+ }
+
+ case RETURN_SMART_STATUS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) );
+ break;
+ }
+
+ case ENABLE_DISABLE_AUTOSAVE:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
+ break;
+ }
+
+ case SAVE_ATTRIBUTE_VALUES:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
+ break;
+ }
+
+
+ case EXECUTE_OFFLINE_DIAGS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
+ break;
+ }
+
+ case READ_ATTRIBUTES:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
+ break;
+ }
+
+ case READ_THRESHOLDS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
+ break;
+ }
+
+ case SMART_READ_LOG:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE) );
+ break;
+ }
+
+ case SMART_WRITE_LOG:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
+ lengthNeeded = lengthNeeded - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE);
+ break;
+ }
+
+ }
+
+ } else if (Command == ID_CMD) {
+
+ controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE );
+
+ } else {
+
+ NT_ASSERT(FALSE);
+ }
+
+ NT_ASSERT(controlCode == SrbControlCode);
+ NT_ASSERT(availableBufferSize >= lengthNeeded);
+ }
+
+#endif
+
+ //
+ // Build SrbControl and input to SMART command
+ //
+ SrbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+ RtlMoveMemory (SrbControl->Signature, "SCSIDISK", 8);
+ SrbControl->Timeout = FdoExtension->TimeOutValue;
+ SrbControl->Length = availableBufferSize;
+ SrbControl->ControlCode = SrbControlCode;
+
+ cmdInParameters->cBufferSize = sizeof(SENDCMDINPARAMS);
+ cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
+ cmdInParameters->irDriveRegs.bFeaturesReg = Feature;
+ cmdInParameters->irDriveRegs.bSectorCountReg = SectorCount;
+ cmdInParameters->irDriveRegs.bSectorNumberReg = SectorNumber;
+ cmdInParameters->irDriveRegs.bCylLowReg = SMART_CYL_LOW;
+ cmdInParameters->irDriveRegs.bCylHighReg = SMART_CYL_HI;
+ cmdInParameters->irDriveRegs.bCommandReg = Command;
+
+ //
+ // Create and send irp
+ //
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ startingOffset.QuadPart = (LONGLONG) 1;
+
+ length = SrbControl->HeaderLength + SrbControl->Length;
+
+ irp = IoBuildSynchronousFsdRequest(
+ IRP_MJ_SCSI,
+ commonExtension->LowerDeviceObject,
+ SrbControl,
+ length,
+ &startingOffset,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set major and minor codes.
+ //
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ irpStack->MinorFunction = 1;
+
+ //
+ // Fill in SRB fields.
+ //
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ irpStack->Parameters.Others.Argument1 = srbEx;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(srbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_IO_CONTROL;
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+
+ srbEx->SrbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ srbEx->OriginalRequest = irp;
+
+ //
+ // Set timeout to requested value.
+ //
+
+ srbEx->TimeOutValue = SrbControl->Timeout;
+
+ //
+ // Set the data buffer.
+ //
+
+ srbEx->DataBuffer = SrbControl;
+ srbEx->DataTransferLength = length;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+ storAddrBtl8->Path = diskData->ScsiAddress.PathId;
+ storAddrBtl8->Target = diskData->ScsiAddress.TargetId;
+ storAddrBtl8->Lun = srb.Lun = diskData->ScsiAddress.Lun;
+
+ } else {
+ irpStack->Parameters.Others.Argument1 = &srb;
+
+ srb.PathId = diskData->ScsiAddress.PathId;
+ srb.TargetId = diskData->ScsiAddress.TargetId;
+ srb.Lun = diskData->ScsiAddress.Lun;
+
+ srb.Function = SRB_FUNCTION_IO_CONTROL;
+ srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb.SrbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+
+ srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb.QueueTag = SP_UNTAGGED;
+
+ srb.OriginalRequest = irp;
+
+ //
+ // Set timeout to requested value.
+ //
+
+ srb.TimeOutValue = SrbControl->Timeout;
+
+ //
+ // Set the data buffer.
+ //
+
+ srb.DataBuffer = SrbControl;
+ srb.DataTransferLength = length;
+ }
+
+ //
+ // Flush the data buffer for output. This will insure that the data is
+ // written back to memory. Since the data-in flag is the the port driver
+ // will flush the data again for input which will ensure the data is not
+ // in the cache.
+ //
+
+ KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
+
+ //
+ // Call port driver to handle this request.
+ //
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskGetIdentifyInfo(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PBOOLEAN SupportSmart
+ )
+{
+ UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE )] = {0};
+ ULONG outBufferSize = sizeof(outBuffer);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ status = DiskGetIdentifyData(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ PUSHORT identifyData = (PUSHORT)&(outBuffer[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1]);
+ USHORT commandSetSupported = identifyData[82];
+
+ *SupportSmart = ((commandSetSupported != 0xffff) &&
+ (commandSetSupported != 0) &&
+ ((commandSetSupported & 1) == 1));
+ } else {
+ *SupportSmart = FALSE;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskGetIdentifyInfo: SMART %s supported for device %p, status %lx\n",
+ *SupportSmart ? "is" : "is not",
+ FdoExtension->DeviceObject,
+ status));
+
+ return status;
+}
+
+
+//
+// FP Ioctl specific routines
+//
+
+NTSTATUS
+DiskSendFailurePredictIoctl(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_PREDICT_FAILURE checkFailure
+ )
+{
+ KEVENT event;
+ PDEVICE_OBJECT deviceObject;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ PIRP irp;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ deviceObject = IoGetAttachedDeviceReference(FdoExtension->DeviceObject);
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_STORAGE_PREDICT_FAILURE,
+ deviceObject,
+ NULL,
+ 0,
+ checkFailure,
+ sizeof(STORAGE_PREDICT_FAILURE),
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL)
+ {
+ status = IoCallDriver(deviceObject, irp);
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ObDereferenceObject(deviceObject);
+
+ return status;
+}
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+NTSTATUS
+DiskGetModePage(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl,
+ _In_ PMODE_PARAMETER_HEADER ModeData,
+ _Inout_ PULONG ModeDataSize,
+ _Out_ PVOID* PageData
+ )
+{
+ ULONG size = 0;
+ PVOID pageData = NULL;
+
+ PAGED_CODE();
+
+ if (ModeData == NULL ||
+ ModeDataSize == NULL ||
+ *ModeDataSize < sizeof(MODE_PARAMETER_HEADER) ||
+ PageData == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ RtlZeroMemory (ModeData, *ModeDataSize);
+
+ size = ClassModeSenseEx(Fdo,
+ (PCHAR) ModeData,
+ *ModeDataSize,
+ PageMode,
+ PageControl);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+ size = ClassModeSenseEx(Fdo,
+ (PCHAR) ModeData,
+ *ModeDataSize,
+ PageMode,
+ PageControl);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetModePage: Mode Sense for Page Mode %d with Page Control %d failed\n",
+ PageMode, PageControl));
+ *ModeDataSize = 0;
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+ if (size > (ULONG) (ModeData->ModeDataLength + 1)) {
+ size = ModeData->ModeDataLength + 1;
+ }
+
+ *ModeDataSize = size;
+
+ //
+ // Find the mode page
+ //
+ pageData = ClassFindModePage((PCHAR) ModeData,
+ size,
+ PageMode,
+ TRUE);
+
+ if (pageData) {
+ *PageData = pageData;
+ return STATUS_SUCCESS;
+ } else {
+ *PageData = NULL;
+ return STATUS_NOT_SUPPORTED;
+ }
+}
+
+NTSTATUS
+DiskEnableInfoExceptions(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN Enable
+ )
+{
+ PDISK_DATA diskData = (PDISK_DATA)(FdoExtension->CommonExtension.DriverData);
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_INFO_EXCEPTIONS pageData;
+ MODE_INFO_EXCEPTIONS changeablePageData;
+ ULONG modeDataSize;
+
+ PAGED_CODE();
+
+ modeDataSize = MODE_DATA_SIZE;
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ modeDataSize,
+ DISK_TAG_INFO_EXCEPTION);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // First see which data is actually changeable.
+ //
+ status = DiskGetModePage(FdoExtension->DeviceObject,
+ MODE_PAGE_FAULT_REPORTING,
+ 1, // Page Control = 1 indicates we want changeable values.
+ modeData,
+ &modeDataSize,
+ (PVOID*)&pageData);
+
+ if (!NT_SUCCESS(status) || pageData == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support SMART for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: DOES support SMART for device %p\n",
+ FdoExtension->DeviceObject));
+
+ //
+ // At the very least, the DEXCPT bit must be changeable.
+ // If it's not, bail out now.
+ //
+ if (pageData->Dexcpt == 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support DEXCPT bit for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Cache away which values are changeable.
+ //
+ RtlCopyMemory(&changeablePageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
+
+ //
+ // Now get the current values.
+ //
+ status = DiskGetModePage(FdoExtension->DeviceObject,
+ MODE_PAGE_FAULT_REPORTING,
+ 0, // Page Control = 0 indicates we want current values.
+ modeData,
+ &modeDataSize,
+ (PVOID*)&pageData);
+
+ if (!NT_SUCCESS(status) || pageData == NULL) {
+ //
+ // At this point we know the device supports this mode page so
+ // assert if something goes wrong here.
+ //
+ NT_ASSERT(NT_SUCCESS(status) && pageData);
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If the device is currently configured to not report any informational
+ // exceptions and we cannot change the value of that field, there's
+ // nothing to be done.
+ //
+ if (pageData->ReportMethod == 0 && changeablePageData.ReportMethod == 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: MRIE field is 0 and is not changeable for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If the PERF bit is changeable, set it now.
+ //
+ if (changeablePageData.Perf) {
+ pageData->Perf = diskData->AllowFPPerfHit ? 0 : 1;
+ }
+
+ //
+ // If the MRIE field is changeable, set it to 4 so that informational
+ // exceptions get reported with the "Recovered Error" sense key.
+ //
+ if (changeablePageData.ReportMethod) {
+ pageData->ReportMethod = 4;
+ }
+
+ //
+ // Finally, set the DEXCPT bit appropriately to enable/disable
+ // informational exception reporting and send the Mode Select.
+ //
+ pageData->Dexcpt = !Enable;
+
+ status = ClassModeSelect(FdoExtension->DeviceObject,
+ (PCHAR)modeData,
+ modeDataSize,
+ pageData->PSBit);
+
+ //
+ // Update the failure prediction state. Note that for this particular
+ // mode FailurePredictionNone is used when it's not enabled.
+ //
+ if (NT_SUCCESS(status)) {
+ if (Enable) {
+ diskData->FailurePredictionCapability = FailurePredictionSense;
+ diskData->FailurePredictionEnabled = TRUE;
+ } else {
+ diskData->FailurePredictionCapability = FailurePredictionNone;
+ diskData->FailurePredictionEnabled = FALSE;
+ }
+ }
+
+ FREE_POOL(modeData);
+
+ return status;
+}
+#endif
+
+
+//
+// FP type independent routines
+//
+
+NTSTATUS
+DiskEnableDisableFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable
+ )
+/*++
+
+Routine Description:
+
+ Enable or disable failure prediction at the hardware level
+
+Arguments:
+
+ FdoExtension
+
+ Enable
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ NTSTATUS status;
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ if (Enable)
+ {
+ status = DiskEnableSmart(FdoExtension);
+ } else {
+ status = DiskDisableSmart(FdoExtension);
+ }
+
+ if (NT_SUCCESS(status)) {
+ diskData->FailurePredictionEnabled = Enable;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ case FailurePredictionIoctl:
+ {
+ //
+ // We assume that the drive is already setup properly for
+ // failure prediction
+ //
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ }
+ return status;
+}
+
+
+NTSTATUS
+DiskEnableDisableFailurePredictPolling(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable,
+ ULONG PollTimeInSeconds
+ )
+/*++
+
+Routine Description:
+
+ Enable or disable polling for hardware failure detection
+
+Arguments:
+
+ FdoExtension
+
+ Enable
+
+ PollTimeInSeconds - if 0 then no change to current polling timer
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ NTSTATUS status;
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ if (Enable)
+ {
+ status = DiskEnableDisableFailurePrediction(FdoExtension,
+ Enable);
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ status = ClassSetFailurePredictionPoll(FdoExtension,
+ Enable ? diskData->FailurePredictionCapability :
+ FailurePredictionNone,
+ PollTimeInSeconds);
+
+ //
+ // Even if this failed we do not want to disable FP on the
+ // hardware. FP is only ever disabled on the hardware by
+ // specific command of the user.
+ //
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictStatus(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction status
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartStatus
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DiskSmartStatus->PredictFailure = FALSE;
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) )] = {0};
+ ULONG outBufferSize = sizeof(outBuffer);
+ PSENDCMDOUTPARAMS cmdOutParameters;
+
+ status = DiskReadSmartStatus(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ DiskSmartStatus->Reason = 0; // Unknown;
+ DiskSmartStatus->PredictFailure = ((cmdOutParameters->bBuffer[3] == 0xf4) &&
+ (cmdOutParameters->bBuffer[4] == 0x2c));
+ }
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ DiskSmartStatus->Reason = FdoExtension->FailureReason;
+ DiskSmartStatus->PredictFailure = FdoExtension->FailurePredicted;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictData(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction data. Not available for
+ FAILURE_PREDICT_SENSE types.
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartData
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ PUCHAR outBuffer;
+ ULONG outBufferSize;
+ PSENDCMDOUTPARAMS cmdOutParameters;
+
+ outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
+
+ outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ outBufferSize,
+ DISK_TAG_SMART);
+
+ if (outBuffer != NULL)
+ {
+ status = DiskReadSmartData(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ DiskSmartData->Length = READ_ATTRIBUTE_BUFFER_SIZE;
+ RtlCopyMemory(DiskSmartData->VendorSpecific,
+ cmdOutParameters->bBuffer,
+ min(READ_ATTRIBUTE_BUFFER_SIZE, sizeof(DiskSmartData->VendorSpecific)));
+ }
+ FREE_POOL(outBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ DiskSmartData->Length = sizeof(ULONG);
+ *((PULONG)DiskSmartData->VendorSpecific) = FdoExtension->FailureReason;
+
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictThresholds(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction thresholds. Not available for
+ FAILURE_PREDICT_SENSE types.
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartData
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ PUCHAR outBuffer;
+ PSENDCMDOUTPARAMS cmdOutParameters;
+ ULONG outBufferSize;
+
+ outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
+
+ outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ outBufferSize,
+ DISK_TAG_SMART);
+
+ if (outBuffer != NULL)
+ {
+ status = DiskReadSmartThresholds(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ RtlCopyMemory(DiskSmartThresholds->VendorSpecific,
+ cmdOutParameters->bBuffer,
+ min(READ_THRESHOLD_BUFFER_SIZE, sizeof(DiskSmartThresholds->VendorSpecific)));
+ }
+ FREE_POOL(outBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+VOID
+DiskReregWorker(
+ IN PDEVICE_OBJECT DevObject,
+ IN PVOID Context
+ )
+{
+ PDISKREREGREQUEST reregRequest;
+ NTSTATUS status;
+ PDEVICE_OBJECT deviceObject;
+ PIRP irp;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(DevObject);
+
+ NT_ASSERT(Context != NULL);
+ _Analysis_assume_(Context != NULL);
+
+ do
+ {
+ reregRequest = (PDISKREREGREQUEST)ExInterlockedPopEntryList(
+ &DiskReregHead,
+ &DiskReregSpinlock);
+
+ if (reregRequest != NULL)
+ {
+ deviceObject = reregRequest->DeviceObject;
+ irp = reregRequest->Irp;
+
+ status = IoWMIRegistrationControl(deviceObject,
+ WMIREG_ACTION_UPDATE_GUIDS);
+
+ //
+ // Release remove lock and free irp, now that we are done
+ // processing this
+ //
+ ClassReleaseRemoveLock(deviceObject, irp);
+
+ IoFreeMdl(irp->MdlAddress);
+ IoFreeIrp(irp);
+
+ FREE_POOL(reregRequest);
+
+ } else {
+
+ NT_ASSERTMSG("Disk Re-registration request list should not be empty", FALSE);
+
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskReregWorker: Reregistration failed %x\n",
+ status));
+ }
+
+ } while (InterlockedDecrement(&DiskReregWorkItems));
+
+ IoFreeWorkItem((PIO_WORKITEM)Context);
+}
+
+
+NTSTATUS
+DiskInitializeReregistration(
+ VOID
+ )
+{
+ PAGED_CODE();
+
+ //
+ // Initialize the spinlock used to manage the
+ // list of disks reregistering their guids
+ //
+ KeInitializeSpinLock(&DiskReregSpinlock);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+DiskPostReregisterRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PDISKREREGREQUEST reregRequest;
+ PIO_WORKITEM workItem;
+ NTSTATUS status;
+
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (!workItem) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ reregRequest = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(DISKREREGREQUEST),
+ DISK_TAG_SMART);
+ if (reregRequest != NULL)
+ {
+ //
+ // add the disk that needs reregistration to the stack of disks
+ // to reregister. If the list is transitioning from empty to
+ // non empty then also kick off the work item so that the
+ // reregistration worker can do the reregister.
+ //
+ reregRequest->DeviceObject = DeviceObject;
+ reregRequest->Irp = Irp;
+ ExInterlockedPushEntryList(
+ &DiskReregHead,
+ &reregRequest->Next,
+ &DiskReregSpinlock);
+
+ if (InterlockedIncrement(&DiskReregWorkItems) == 1)
+ {
+ //
+ // There is no worker routine running, queue this one.
+ // When the work item runs, it will process the reregistration
+ // list.
+ //
+
+ IoQueueWorkItem(workItem,
+ DiskReregWorker,
+ DelayedWorkQueue,
+ workItem);
+ } else {
+
+ //
+ // There is a worker routine already running, so we
+ // can free this unused work item.
+ //
+
+ IoFreeWorkItem(workItem);
+ }
+
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ IoFreeWorkItem(workItem);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskPostReregisterRequest: could not allocate reregRequest for %p\n",
+ DeviceObject));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return(status);
+}
+
+
+NTSTATUS
+DiskInfoExceptionComplete(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+ NTSTATUS status;
+ BOOLEAN retry;
+ ULONG retryInterval;
+ ULONG srbStatus;
+ BOOLEAN freeLockAndIrp = TRUE;
+ PVOID originalSenseInfoBuffer = irpStack->Parameters.Others.Argument3;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PVOID dataBuffer = NULL;
+ ULONG dataLength = 0;
+ PVOID senseBuffer = NULL;
+ UCHAR cdbLength8 = 0;
+ ULONG cdbLength32 = 0;
+ UCHAR senseBufferLength = 0;
+
+ srbStatus = SRB_STATUS(srb->SrbStatus);
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+ dataBuffer = srbEx->DataBuffer;
+ dataLength = srbEx->DataTransferLength;
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ (void)GetSrbScsiData(srbEx, &cdbLength8, &cdbLength32, NULL, &senseBuffer, &senseBufferLength);
+ }
+ } else {
+ dataBuffer = srb->DataBuffer;
+ dataLength = srb->DataTransferLength;
+ senseBuffer = srb->SenseInfoBuffer;
+ }
+
+ //
+ // Check SRB status for success of completing request.
+ // SRB_STATUS_DATA_OVERRUN also indicates success.
+ //
+ if ((srbStatus != SRB_STATUS_SUCCESS) &&
+ (srbStatus != SRB_STATUS_DATA_OVERRUN))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: IRP %p, SRB %p\n", Irp, srb));
+
+ if (TEST_FLAG(srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN))
+ {
+ ClassReleaseQueue(DeviceObject);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ DeviceObject,
+ srb,
+ irpStack->MajorFunction,
+ 0,
+ MAXIMUM_RETRIES -
+ ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ status == STATUS_VERIFY_REQUIRED)
+ {
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ retry = retry && irpStack->Parameters.Others.Argument4;
+
+ irpStack->Parameters.Others.Argument4 = (PVOID)((ULONG_PTR)irpStack->Parameters.Others.Argument4 - 1);
+
+ if (retry)
+ {
+ //
+ // Retry request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: Retry request %p\n", Irp));
+
+ NT_ASSERT(dataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress));
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+ srbEx->DataTransferLength = Irp->MdlAddress->ByteCount;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srbEx->SrbStatus = 0;
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ SetSrbScsiData(srbEx, cdbLength8, cdbLength32, 0, senseBuffer, senseBufferLength);
+ }
+
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ //
+
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(srbEx->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ } else {
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+ srb->DataTransferLength = Irp->MdlAddress->ByteCount;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srb->SrbStatus = srb->ScsiStatus = 0;
+
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ //
+
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->QueueTag = SP_UNTAGGED;
+ }
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = srb;
+
+
+ IoSetCompletionRoutine(Irp,
+ DiskInfoExceptionComplete,
+ srb,
+ TRUE, TRUE, TRUE);
+
+ (VOID)IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Get the results from the mode sense
+ //
+ PMODE_INFO_EXCEPTIONS pageData;
+ PMODE_PARAMETER_HEADER modeData;
+ ULONG modeDataLength;
+
+ modeData = dataBuffer;
+ modeDataLength = dataLength;
+
+ pageData = ClassFindModePage((PCHAR)modeData,
+ modeDataLength,
+ MODE_PAGE_FAULT_REPORTING,
+ TRUE);
+ if (pageData != NULL)
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p supports SMART\n",
+ DeviceObject));
+
+ diskData->ScsiInfoExceptionsSupported = TRUE;
+
+ //
+ // The DEXCPT bit must be 0 and the MRIE field must be valid.
+ //
+ if (pageData->Dexcpt == 0 &&
+ pageData->ReportMethod >= 2 &&
+ pageData->ReportMethod <= 6)
+ {
+ diskData->FailurePredictionCapability = FailurePredictionSense;
+ diskData->FailurePredictionEnabled = TRUE;
+ status = DiskPostReregisterRequest(DeviceObject, Irp);
+
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Make sure we won't free the remove lock and the irp
+ // since we need to keep these until after the work
+ // item has completed running
+ //
+ freeLockAndIrp = FALSE;
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p is not enabled for SMART\n",
+ DeviceObject));
+
+ }
+
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p does not supports SMART\n",
+ DeviceObject));
+
+ }
+
+ //
+ // Set status for successful request
+ //
+
+ status = STATUS_SUCCESS;
+
+ } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
+
+ //
+ // Free the srb
+ //
+ if (senseBuffer != originalSenseInfoBuffer)
+ {
+ //
+ // Free the original sense info buffer in case the port driver has overwritten it
+ //
+ FREE_POOL(originalSenseInfoBuffer);
+ }
+
+ FREE_POOL(senseBuffer);
+ FREE_POOL(dataBuffer);
+ FREE_POOL(srb);
+
+ if (freeLockAndIrp)
+ {
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // If pending has be returned for this irp then mark the current stack as
+ // pending.
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ IoFreeMdl(Irp->MdlAddress);
+ IoFreeIrp(Irp);
+ }
+
+ return(STATUS_MORE_PROCESSING_REQUIRED);
+}
+
+
+NTSTATUS
+DiskInfoExceptionCheck(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PUCHAR modeData;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ PVOID senseInfoBuffer = NULL;
+ UCHAR senseInfoBufferLength = 0;
+ ULONG isRemoved;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_INFO_EXCEPTION);
+ if (modeData == NULL)
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate mode data "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+ if (srb == NULL)
+ {
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate srb "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Sense buffer is in aligned nonpaged pool.
+ //
+
+ senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ SENSE_BUFFER_SIZE_EX,
+ '7CcS');
+
+ if (senseInfoBuffer == NULL)
+ {
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate request sense "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
+
+ //
+ // Build device I/O control request with METHOD_NEITHER data transfer.
+ // We'll queue a completion routine to cleanup the MDL's and such ourself.
+ //
+
+ irp = IoAllocateIrp(
+ (CCHAR) (FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
+ FALSE);
+
+ if (irp == NULL)
+ {
+ FREE_POOL(senseInfoBuffer);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate Irp\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ isRemoved = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
+
+ if (isRemoved)
+ {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ IoFreeIrp(irp);
+ FREE_POOL(senseInfoBuffer);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: RemoveLock says isRemoved\n"));
+ return(STATUS_DEVICE_DOES_NOT_EXIST);
+ }
+
+ //
+ // Get next stack location.
+ //
+
+ IoSetNextIrpStackLocation(irp);
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+ irpStack->DeviceObject = FdoExtension->DeviceObject;
+
+ //
+ // Save retry count in current Irp stack.
+ //
+ irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Save allocated sense info buffer in case the port driver overwrites it
+ //
+ irpStack->Parameters.Others.Argument3 = senseInfoBuffer;
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set up SRB for execute scsi request. Save SRB address in next stack
+ // for the port driver.
+ //
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ IoSetCompletionRoutine(irp,
+ DiskInfoExceptionComplete,
+ srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irp->MdlAddress = IoAllocateMdl( modeData,
+ MODE_DATA_SIZE,
+ FALSE,
+ FALSE,
+ irp );
+ if (irp->MdlAddress == NULL)
+ {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ FREE_POOL(senseInfoBuffer);
+ IoFreeIrp( irp );
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Can't allocate MDL\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MmBuildMdlForNonPagedPool(irp->MdlAddress);
+
+ //
+ // Build the MODE SENSE CDB.
+ //
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value from device extension.
+ srbEx->TimeOutValue = FdoExtension->TimeOutValue;
+
+ // Set the transfer length.
+ srbEx->DataTransferLength = MODE_DATA_SIZE;
+ srbEx->DataBuffer = modeData;
+
+ srbEx->SrbFlags = FdoExtension->SrbFlags;
+
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Don't freeze the queue on an error
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ // Set up IRP Address.
+ srbEx->OriginalRequest = irp;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ // Enable auto request sense.
+ srbExDataCdb16->SenseInfoBufferLength = senseInfoBufferLength;
+ srbExDataCdb16->SenseInfoBuffer = senseInfoBuffer;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ FREE_POOL(senseInfoBuffer);
+ IoFreeIrp( irp );
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Insufficient extended SRB size\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+
+ //
+ // Write length to SRB.
+ //
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+
+ //
+ // Set SCSI bus address.
+ //
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Enable auto request sense.
+ //
+
+ srb->SenseInfoBufferLength = senseInfoBufferLength;
+ srb->SenseInfoBuffer = senseInfoBuffer;
+
+ //
+ // Set timeout value from device extension.
+ //
+ srb->TimeOutValue = FdoExtension->TimeOutValue;
+
+ //
+ // Set the transfer length.
+ //
+ srb->DataTransferLength = MODE_DATA_SIZE;
+ srb->DataBuffer = modeData;
+
+ srb->SrbFlags = FdoExtension->SrbFlags;
+
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Don't freeze the queue on an error
+ //
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->QueueTag = SP_UNTAGGED;
+
+ //
+ // Set up IRP Address.
+ //
+ srb->OriginalRequest = irp;
+
+ srb->CdbLength = 6;
+ cdb = (PCDB)srb->Cdb;
+
+ }
+
+ cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ cdb->MODE_SENSE.PageCode = MODE_PAGE_FAULT_REPORTING;
+ cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ IoMarkIrpPending(irp);
+ IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject,
+ irp);
+
+ return(STATUS_PENDING);
+}
+
+
+NTSTATUS
+DiskDetectFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PFAILURE_PREDICTION_METHOD FailurePredictCapability,
+ BOOLEAN ScsiAddressAvailable
+ )
+/*++
+
+Routine Description:
+
+ Detect if device has any failure prediction capabilities. First we
+ check for IDE SMART capability. This is done by sending the drive an
+ IDENTIFY command and checking if the SMART command set bit is set.
+
+ Next we check if SCSI SMART (aka Information Exception Control Page,
+ X3T10/94-190 Rev 4). This is done by querying for the Information
+ Exception mode page.
+
+ Lastly we check if the device has IOCTL failure prediction. This mechanism
+ a filter driver implements IOCTL_STORAGE_PREDICT_FAILURE and will respond
+ with the information in the IOCTL. We do this by sending the ioctl and
+ if the status returned is STATUS_SUCCESS we assume that it is supported.
+
+Arguments:
+
+ FdoExtension
+
+ *FailurePredictCapability
+
+ ScsiAddressAvailable TRUE if there is a valid SCSI_ADDRESS available
+ for this device, FALSE otherwise.
+ If FALSE we do not allow SMART IOCTLs (FailurePredictionSmart capability)
+ which require a valid SCSI_ADDRESS. The other capabilities
+ <FailurePredictionIoctl, FailurePredictionSense) do not requere
+ SCSI_ADDRESS so we'll still try to initialize them.
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ BOOLEAN supportFP;
+ NTSTATUS status;
+ STORAGE_PREDICT_FAILURE checkFailure;
+ STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+
+ PAGED_CODE();
+
+ //
+ // Assume no failure predict mechanisms
+ //
+ *FailurePredictCapability = FailurePredictionNone;
+
+ //
+ // See if this is an IDE drive that supports SMART. If so enable SMART
+ // and then ensure that it suports the SMART READ STATUS command
+ //
+
+ if (ScsiAddressAvailable)
+ {
+ DiskGetIdentifyInfo(FdoExtension, &supportFP);
+
+ if (supportFP)
+ {
+ status = DiskEnableSmart(FdoExtension);
+ if (NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionSmart;
+ diskData->FailurePredictionEnabled = TRUE;
+
+ status = DiskReadFailurePredictStatus(FdoExtension,
+ &diskSmartStatus);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IDE SMART\n",
+ FdoExtension->DeviceObject,
+ NT_SUCCESS(status) ? "does" : "does not"));
+
+ if (!NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionNone;
+ diskData->FailurePredictionEnabled = FALSE;
+ }
+ }
+ return(status);
+ }
+ }
+ //
+ // See if there is a a filter driver to intercept
+ // IOCTL_STORAGE_PREDICT_FAILURE
+ //
+ status = DiskSendFailurePredictIoctl(FdoExtension,
+ &checkFailure);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IOCTL_STORAGE_FAILURE_PREDICT\n",
+ FdoExtension->DeviceObject,
+ NT_SUCCESS(status) ? "does" : "does not"));
+
+ if (NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionIoctl;
+ diskData->FailurePredictionEnabled = TRUE;
+ if (checkFailure.PredictFailure)
+ {
+ checkFailure.PredictFailure = 512;
+ ClassNotifyFailurePredicted(FdoExtension,
+ (PUCHAR)&checkFailure,
+ sizeof(checkFailure),
+ (BOOLEAN)(FdoExtension->FailurePredicted == FALSE),
+ 0x11,
+ diskData->ScsiAddress.PathId,
+ diskData->ScsiAddress.TargetId,
+ diskData->ScsiAddress.Lun);
+
+ FdoExtension->FailurePredicted = TRUE;
+ }
+ return(status);
+ }
+
+ //
+ // Finally we assume it will not be a scsi smart drive. but
+ // we'll also send off an asynchronous mode sense so that if
+ // it is SMART we'll reregister the device object
+ //
+
+ *FailurePredictCapability = FailurePredictionNone;
+
+ DiskInfoExceptionCheck(FdoExtension);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+DiskWmiFunctionControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN CLASSENABLEDISABLEFUNCTION Function,
+ IN BOOLEAN Enable
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to enabled or disable event
+ generation or data block collection. A device should only expect a
+ single enable when the first event or data consumer enables events or
+ data collection and a single disable when the last event or data
+ consumer disables events or data collection. Data blocks will only
+ receive collection enable/disable if they were registered as requiring
+ it.
+
+
+ When NT boots, failure prediction is not automatically enabled, although
+ it may have been persistantly enabled on a previous boot. Polling is also
+ not automatically enabled. When the first data block that accesses SMART
+ such as SmartStatusGuid, SmartDataGuid, SmartPerformFunction, or
+ SmartEventGuid is accessed then SMART is automatically enabled in the
+ hardware. Polling is enabled when SmartEventGuid is enabled and disabled
+ when it is disabled. Hardware SMART is only disabled when the DisableSmart
+ method is called. Polling is also disabled when this is called regardless
+ of the status of the other guids or events.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ Function specifies which functionality is being enabled or disabled
+
+ Enable is TRUE then the function is being enabled else disabled
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PAGED_CODE();
+
+ if ((Function == DataBlockCollection) && Enable)
+ {
+ if ((GuidIndex == SmartStatusGuid) ||
+ (GuidIndex == SmartDataGuid) ||
+ (GuidIndex == SmartThresholdsGuid) ||
+ (GuidIndex == SmartPerformFunction))
+ {
+ status = DiskEnableDisableFailurePrediction(fdoExtension,
+ TRUE);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p Enable -> %lx\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Collection\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled"));
+ }
+ } else if (Function == EventGeneration) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Event Generation\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled"));
+
+
+ if ((GuidIndex == SmartEventGuid) && Enable)
+ {
+ status = DiskEnableDisableFailurePredictPolling(fdoExtension,
+ Enable,
+ 0);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p %s -> %lx\n",
+ DeviceObject,
+ Irp,
+ Enable ? "DiskEnableSmartPolling" : "DiskDisableSmartPolling",
+ status));
+ }
+
+#if DBG
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for function %d\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled",
+ Function));
+#endif
+ }
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+ return status;
+}
+
+
+
+NTSTATUS
+DiskFdoQueryWmiRegInfo(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to retrieve the list of
+ guids or data blocks that the driver wants to register with WMI. This
+ routine may not pend or block. Driver should NOT call
+ ClassWmiCompleteRequest.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ *RegFlags returns with a set of flags that describe the guids being
+ registered for this device. If the device wants enable and disable
+ collection callbacks before receiving queries for the registered
+ guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
+ returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
+ the instance name is determined from the PDO associated with the
+ device object. Note that the PDO must have an associated devnode. If
+ WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
+ name for the device.
+
+ InstanceName returns with the instance name for the guids if
+ WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
+ caller will call ExFreePool with the buffer returned.
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(InstanceName);
+
+ SET_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+
+ switch (diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ //
+ // Fall Through
+ //
+ }
+ case FailurePredictionIoctl:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ break;
+ }
+
+
+ default:
+ {
+ SET_FLAG (DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+ break;
+ }
+ }
+
+ //
+ // Use devnode for FDOs
+ *RegFlags = WMIREG_FLAG_INSTANCE_PDO;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DiskFdoQueryWmiRegInfoEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName,
+ OUT PUNICODE_STRING MofName
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to retrieve the list of
+ guids or data blocks that the driver wants to register with WMI. This
+ routine may not pend or block. Driver should NOT call
+ ClassWmiCompleteRequest.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ *RegFlags returns with a set of flags that describe the guids being
+ registered for this device. If the device wants enable and disable
+ collection callbacks before receiving queries for the registered
+ guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
+ returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
+ the instance name is determined from the PDO associated with the
+ device object. Note that the PDO must have an associated devnode. If
+ WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
+ name for the device.
+
+ InstanceName returns with the instance name for the guids if
+ WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
+ caller will call ExFreePool with the buffer returned.
+
+ MofName returns initialized with the mof resource name for the
+ binary mof resource attached to the driver's image file. If the
+ driver does not have a mof resource then it should leave this
+ parameter untouched.
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(MofName);
+
+ status = DiskFdoQueryWmiRegInfo(DeviceObject,
+ RegFlags,
+ InstanceName);
+
+ //
+ // Leave MofName alone since disk doesn't have one
+ //
+ return(status);
+}
+
+
+NTSTATUS
+DiskFdoQueryWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferAvail,
+ OUT PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ Irp is the Irp that makes this request
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ BufferAvail on has the maximum size available to write the data
+ block.
+
+ Buffer on return is filled with the returned data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ ULONG sizeNeeded;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
+ " BufferAvail %lx Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, BufferAvail, Buffer));
+
+ switch (GuidIndex)
+ {
+ case DiskGeometryGuid:
+ {
+ sizeNeeded = sizeof(DISK_GEOMETRY);
+ if (BufferAvail >= sizeNeeded)
+ {
+ if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+ {
+ //
+ // Issue ReadCapacity to update device extension
+ // with information for current media.
+ status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ //
+ // Note whether the drive is ready.
+ diskData->ReadyStatus = status;
+
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+ }
+
+ //
+ // Copy drive geometry information from device extension.
+ RtlMoveMemory(Buffer,
+ &(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ case SmartStatusGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+
+ NT_ASSERT(diskData->FailurePredictionCapability != FailurePredictionNone);
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_STATUS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ STORAGE_PREDICT_FAILURE checkFailure;
+
+ diskSmartStatus = (PSTORAGE_FAILURE_PREDICT_STATUS)Buffer;
+
+ status = DiskSendFailurePredictIoctl(fdoExtension,
+ &checkFailure);
+
+ if (NT_SUCCESS(status))
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSense)
+ {
+ diskSmartStatus->Reason = *((PULONG)checkFailure.VendorSpecific);
+ } else {
+ diskSmartStatus->Reason = 0; // unknown
+ }
+
+ diskSmartStatus->PredictFailure = (checkFailure.PredictFailure != 0);
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ case SmartDataGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_DATA diskSmartData;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionIoctl));
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_DATA);
+ if (BufferAvail >= sizeNeeded)
+ {
+ PSTORAGE_PREDICT_FAILURE checkFailure = (PSTORAGE_PREDICT_FAILURE)Buffer;
+
+ diskSmartData = (PSTORAGE_FAILURE_PREDICT_DATA)Buffer;
+
+ status = DiskSendFailurePredictIoctl(fdoExtension,
+ checkFailure);
+
+ if (NT_SUCCESS(status))
+ {
+ diskSmartData->Length = 512;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ case SmartThresholdsGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS diskSmartThresholds;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart));
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_THRESHOLDS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ diskSmartThresholds = (PSTORAGE_FAILURE_PREDICT_THRESHOLDS)Buffer;
+ status = DiskReadFailurePredictThresholds(fdoExtension,
+ diskSmartThresholds);
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ case SmartPerformFunction:
+ {
+ sizeNeeded = 0;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case ScsiInfoExceptionsGuid:
+ {
+ PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
+ MODE_INFO_EXCEPTIONS modeInfo;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSense));
+
+ sizeNeeded = sizeof(STORAGE_SCSI_INFO_EXCEPTIONS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
+ status = DiskGetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ if (NT_SUCCESS(status))
+ {
+ infoExceptions->PageSavable = modeInfo.PSBit;
+ infoExceptions->Flags = modeInfo.Flags;
+ infoExceptions->MRIE = modeInfo.ReportMethod;
+ infoExceptions->Padding = 0;
+ REVERSE_BYTES(&infoExceptions->IntervalTimer,
+ &modeInfo.IntervalTimer);
+ REVERSE_BYTES(&infoExceptions->ReportCount,
+ &modeInfo.ReportCount)
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+ }
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ sizeNeeded,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoSetWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ Irp is the Irp that makes this request
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ BufferSize has the size of the data block passed
+
+ Buffer has the new values for the data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
+ " BufferSize %#x Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, BufferSize, Buffer));
+
+ if (GuidIndex == ScsiInfoExceptionsGuid)
+ {
+ PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
+ MODE_INFO_EXCEPTIONS modeInfo = {0};
+
+ if (BufferSize >= sizeof(STORAGE_SCSI_INFO_EXCEPTIONS))
+ {
+ infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
+
+ modeInfo.PageCode = MODE_PAGE_FAULT_REPORTING;
+ modeInfo.PageLength = sizeof(MODE_INFO_EXCEPTIONS) - 2;
+
+ modeInfo.PSBit = 0;
+ modeInfo.Flags = infoExceptions->Flags;
+
+ modeInfo.ReportMethod = infoExceptions->MRIE;
+
+ REVERSE_BYTES(&modeInfo.IntervalTimer[0],
+ &infoExceptions->IntervalTimer);
+
+ REVERSE_BYTES(&modeInfo.ReportCount[0],
+ &infoExceptions->ReportCount);
+
+ if (modeInfo.Perf == 1)
+ {
+ diskData->AllowFPPerfHit = FALSE;
+ } else {
+ diskData->AllowFPPerfHit = TRUE;
+ }
+
+ status = DiskSetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ } else if (GuidIndex <= SmartThresholdsGuid)
+ {
+ status = STATUS_WMI_READ_ONLY;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoSetWmiDataItem(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG DataItemId,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ Irp is the Irp that makes this request
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ DataItemId has the id of the data item being set
+
+ BufferSize has the size of the data item passed
+
+ Buffer has the new values for the data item
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n"
+ " BufferSize %#x Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, DataItemId, BufferSize, Buffer));
+
+ if (GuidIndex <= SmartThresholdsGuid)
+ {
+ status = STATUS_WMI_READ_ONLY;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoExecuteWmiMethod(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG MethodId,
+ IN ULONG InBufferSize,
+ IN ULONG OutBufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to execute a method. When the
+ driver has finished filling the data block it must call
+ ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ Irp is the Irp that makes this request
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ MethodId has the id of the method being called
+
+ InBufferSize has the size of the data block passed in as the input to
+ the method.
+
+ OutBufferSize on entry has the maximum size available to write the
+ returned data block.
+
+ Buffer is filled with the returned data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ ULONG sizeNeeded = 0;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n"
+ " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer));
+
+ switch(GuidIndex)
+ {
+ case SmartPerformFunction:
+ {
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionIoctl) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionSense));
+
+
+ switch(MethodId)
+ {
+ //
+ // void AllowPerformanceHit([in] boolean Allow)
+ //
+ case AllowDisallowPerformanceHit:
+ {
+ BOOLEAN allowPerfHit;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= sizeof(BOOLEAN))
+ {
+ status = STATUS_SUCCESS;
+
+ allowPerfHit = *((PBOOLEAN)Buffer);
+ if (diskData->AllowFPPerfHit != allowPerfHit)
+ {
+ diskData->AllowFPPerfHit = allowPerfHit;
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSense)
+ {
+ MODE_INFO_EXCEPTIONS modeInfo;
+
+ status = DiskGetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ if (NT_SUCCESS(status))
+ {
+ modeInfo.Perf = allowPerfHit ? 0 : 1;
+ status = DiskSetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ }
+ }
+ else
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: AllowPerformanceHit %x for device %p --> %lx\n",
+ allowPerfHit,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void EnableDisableHardwareFailurePrediction([in] boolean Enable)
+ //
+ case EnableDisableHardwareFailurePrediction:
+ {
+ BOOLEAN enable;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= sizeof(BOOLEAN))
+ {
+ status = STATUS_SUCCESS;
+ enable = *((PBOOLEAN)Buffer);
+ if (!enable)
+ {
+ //
+ // If we are disabling we need to also disable
+ // polling
+ //
+ DiskEnableDisableFailurePredictPolling(
+ fdoExtension,
+ enable,
+ 0);
+ }
+
+ status = DiskEnableDisableFailurePrediction(
+ fdoExtension,
+ enable);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableHardwareFailurePrediction: %x for device %p --> %lx\n",
+ enable,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void EnableDisableFailurePredictionPolling(
+ // [in] uint32 Period,
+ // [in] boolean Enable)
+ //
+ case EnableDisableFailurePredictionPolling:
+ {
+ BOOLEAN enable;
+ ULONG period;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= (sizeof(ULONG) + sizeof(BOOLEAN)))
+ {
+ period = *((PULONG)Buffer);
+ Buffer += sizeof(ULONG);
+ enable = *((PBOOLEAN)Buffer);
+
+ status = DiskEnableDisableFailurePredictPolling(
+ fdoExtension,
+ enable,
+ period);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableFailurePredictionPolling: %x %x for device %p --> %lx\n",
+ enable,
+ period,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void GetFailurePredictionCapability([out] uint32 Capability)
+ //
+ case GetFailurePredictionCapability:
+ {
+ sizeNeeded = sizeof(ULONG);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ status = STATUS_SUCCESS;
+ *((PFAILURE_PREDICTION_METHOD)Buffer) = diskData->FailurePredictionCapability;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: GetFailurePredictionCapability: %x for device %p --> %lx\n",
+ *((PFAILURE_PREDICTION_METHOD)Buffer),
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ //
+ // void EnableOfflineDiags([out] boolean Success);
+ //
+ case EnableOfflineDiags:
+ {
+ sizeNeeded = sizeof(BOOLEAN);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ //
+ // Initiate or resume offline diagnostics.
+ // This may cause a loss of performance
+ // to the disk, but mayincrease the amount
+ // of disk checking.
+ //
+ status = DiskExecuteSmartDiagnostics(fdoExtension,
+ 0);
+
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ *((PBOOLEAN)Buffer) = NT_SUCCESS(status);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableOfflineDiags for device %p --> %lx\n",
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ //
+ // void ReadLogSectors([in] uint8 LogAddress,
+ // [in] uint8 SectorCount,
+ // [out] uint32 Length,
+ // [out, WmiSizeIs("Length")] uint8 LogSectors[]
+ // );
+ //
+ case ReadLogSectors:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if (InBufferSize >= sizeof(READ_LOG_SECTORS_IN))
+ {
+ PREAD_LOG_SECTORS_IN inParams;
+ PREAD_LOG_SECTORS_OUT outParams;
+ ULONG readSize;
+
+ inParams = (PREAD_LOG_SECTORS_IN)Buffer;
+ readSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
+ sizeNeeded = FIELD_OFFSET(READ_LOG_SECTORS_OUT,
+ LogSectors) + readSize;
+
+ if (OutBufferSize >= sizeNeeded)
+ {
+ outParams = (PREAD_LOG_SECTORS_OUT)Buffer;
+ status = DiskReadSmartLog(fdoExtension,
+ inParams->SectorCount,
+ inParams->LogAddress,
+ outParams->LogSectors);
+
+ if (NT_SUCCESS(status))
+ {
+ outParams->Length = readSize;
+ } else {
+ //
+ // SMART command failure is
+ // indicated by successful
+ // execution, but no data returned
+ //
+ outParams->Length = 0;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ // void WriteLogSectors([in] uint8 LogAddress,
+ // [in] uint8 SectorCount,
+ // [in] uint32 Length,
+ // [in, WmiSizeIs("Length")] uint8 LogSectors[],
+ // [out] boolean Success
+ // );
+ case WriteLogSectors:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if ((LONG)InBufferSize >= FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
+ LogSectors))
+ {
+ PWRITE_LOG_SECTORS_IN inParams;
+ PWRITE_LOG_SECTORS_OUT outParams;
+ ULONG writeSize;
+
+ inParams = (PWRITE_LOG_SECTORS_IN)Buffer;
+ writeSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
+ if (InBufferSize >= (FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
+ LogSectors) +
+ writeSize))
+ {
+ sizeNeeded = sizeof(WRITE_LOG_SECTORS_OUT);
+
+ if (OutBufferSize >= sizeNeeded)
+ {
+ outParams = (PWRITE_LOG_SECTORS_OUT)Buffer;
+ status = DiskWriteSmartLog(fdoExtension,
+ inParams->SectorCount,
+ inParams->LogAddress,
+ inParams->LogSectors);
+
+ if (NT_SUCCESS(status))
+ {
+ outParams->Success = TRUE;
+ } else {
+ outParams->Success = FALSE;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ // void ExecuteSelfTest([in] uint8 Subcommand,
+ // [out,
+ // Values{"0", "1", "2"},
+ // ValueMap{"Successful Completion",
+ // "Captive Mode Required",
+ // "Unsuccessful Completion"}
+ // ]
+ // uint32 ReturnCode);
+ case ExecuteSelfTest:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if (InBufferSize >= sizeof(EXECUTE_SELF_TEST_IN))
+ {
+ sizeNeeded = sizeof(EXECUTE_SELF_TEST_OUT);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ PEXECUTE_SELF_TEST_IN inParam;
+ PEXECUTE_SELF_TEST_OUT outParam;
+
+ inParam = (PEXECUTE_SELF_TEST_IN)Buffer;
+ outParam = (PEXECUTE_SELF_TEST_OUT)Buffer;
+
+ if (DiskIsValidSmartSelfTest(inParam->Subcommand))
+ {
+ status = DiskExecuteSmartDiagnostics(fdoExtension,
+ inParam->Subcommand);
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Return self test executed
+ // without a problem
+ //
+ outParam->ReturnCode = 0;
+ } else {
+ //
+ // Return Self test execution
+ // failed status
+ //
+ outParam->ReturnCode = 2;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ //
+ // If self test subcommand requires
+ // captive mode then return that
+ // status
+ //
+ outParam->ReturnCode = 1;
+ status = STATUS_SUCCESS;
+ }
+
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ break;
+ }
+
+ default :
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_ITEMID_NOT_FOUND;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case DiskGeometryGuid:
+ case SmartStatusGuid:
+ case SmartDataGuid:
+ case SmartEventGuid:
+ case SmartThresholdsGuid:
+ case ScsiInfoExceptionsGuid:
+ {
+ sizeNeeded = 0;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ default:
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ sizeNeeded,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
diff --git a/storage/class/disk/src/enum.c b/storage/class/disk/src/enum.c
new file mode 100644
index 00000000..d5521caf
--- /dev/null
+++ b/storage/class/disk/src/enum.c
@@ -0,0 +1,1313 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ pnp.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "disk.h"
+#include "ntintsafe.h"
+
+#ifdef DEBUG_USE_WPP
+#include "enum.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DiskConvertExtendedToLayout)
+#pragma alloc_text(PAGE, DiskConvertPartitionToExtended)
+#pragma alloc_text(PAGE, DiskConvertLayoutToExtended)
+#pragma alloc_text(PAGE, DiskCreatePdo)
+#pragma alloc_text(PAGE, DiskEnumerateDevice)
+#pragma alloc_text(PAGE, DiskUpdateRemovablePartitions)
+#pragma alloc_text(PAGE, DiskUpdatePartitions)
+#pragma alloc_text(PAGE, DiskCreatePdo)
+
+#endif
+
+
+PDRIVE_LAYOUT_INFORMATION
+DiskConvertExtendedToLayout(
+ IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx
+ )
+{
+ ULONG i;
+ ULONG LayoutSize;
+ PDRIVE_LAYOUT_INFORMATION Layout;
+ PPARTITION_INFORMATION Partition;
+ PPARTITION_INFORMATION_EX PartitionEx;
+ NTSTATUS status;
+
+ PAGED_CODE ();
+
+ NT_ASSERT ( LayoutEx );
+
+
+ //
+ // The only valid conversion is from an MBR extended layout structure to
+ // the old structure.
+ //
+
+ if (LayoutEx->PartitionStyle != PARTITION_STYLE_MBR) {
+ NT_ASSERT ( FALSE );
+ return NULL;
+ }
+
+ //
+ // Use safe interger function
+ //
+
+ status = RtlULongMult(LayoutEx->PartitionCount, sizeof(PARTITION_INFORMATION), &LayoutSize);
+ if (!NT_SUCCESS(status)) {
+ return NULL;
+ }
+ status = RtlULongAdd(LayoutSize, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[0]),
+ &LayoutSize);
+ if (!NT_SUCCESS(status)) {
+ return NULL;
+ }
+
+ Layout = ExAllocatePoolWithTag (
+ NonPagedPoolNx,
+ LayoutSize,
+ DISK_TAG_PART_LIST
+ );
+
+ if ( Layout == NULL ) {
+ return NULL;
+ }
+
+ Layout->Signature = LayoutEx->Mbr.Signature;
+ Layout->PartitionCount = LayoutEx->PartitionCount;
+
+ for (i = 0; i < LayoutEx->PartitionCount; i++) {
+
+ Partition = &Layout->PartitionEntry[i];
+ PartitionEx = &LayoutEx->PartitionEntry[i];
+
+ Partition->StartingOffset = PartitionEx->StartingOffset;
+ Partition->PartitionLength = PartitionEx->PartitionLength;
+ Partition->RewritePartition = PartitionEx->RewritePartition;
+ Partition->PartitionNumber = PartitionEx->PartitionNumber;
+
+ Partition->PartitionType = PartitionEx->Mbr.PartitionType;
+ Partition->BootIndicator = PartitionEx->Mbr.BootIndicator;
+ Partition->RecognizedPartition = PartitionEx->Mbr.RecognizedPartition;
+ Partition->HiddenSectors = PartitionEx->Mbr.HiddenSectors;
+ }
+
+ return Layout;
+}
+
+
+VOID
+DiskConvertPartitionToExtended(
+ IN PPARTITION_INFORMATION Partition,
+ OUT PPARTITION_INFORMATION_EX PartitionEx
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a PARTITION_INFORMATION structure to a PARTITION_INFORMATION_EX
+ structure.
+
+Arguments:
+
+ Partition - A pointer to the PARTITION_INFORMATION structure to convert.
+
+ PartitionEx - A pointer to a buffer where the converted
+ PARTITION_INFORMATION_EX structure is to be stored.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE ();
+
+ NT_ASSERT ( PartitionEx != NULL );
+ NT_ASSERT ( Partition != NULL );
+
+ PartitionEx->PartitionStyle = PARTITION_STYLE_MBR;
+ PartitionEx->StartingOffset = Partition->StartingOffset;
+ PartitionEx->PartitionLength = Partition->PartitionLength;
+ PartitionEx->RewritePartition = Partition->RewritePartition;
+ PartitionEx->PartitionNumber = Partition->PartitionNumber;
+
+ PartitionEx->Mbr.PartitionType = Partition->PartitionType;
+ PartitionEx->Mbr.BootIndicator = Partition->BootIndicator;
+ PartitionEx->Mbr.RecognizedPartition = Partition->RecognizedPartition;
+ PartitionEx->Mbr.HiddenSectors = Partition->HiddenSectors;
+}
+
+
+PDRIVE_LAYOUT_INFORMATION_EX
+DiskConvertLayoutToExtended(
+ IN CONST PDRIVE_LAYOUT_INFORMATION Layout
+ )
+
+/*++
+
+Routine Description:
+
+ Convert a DRIVE_LAYOUT_INFORMATION structure into a
+ DRIVE_LAYOUT_INFORMATION_EX structure.
+
+Arguments:
+
+ Layout - The source DRIVE_LAYOUT_INFORMATION structure.
+
+Return Values:
+
+ The resultant DRIVE_LAYOUT_INFORMATION_EX structure. This buffer must
+ be freed by the callee using ExFreePool.
+
+--*/
+
+{
+ ULONG i;
+ ULONG size;
+ PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
+ NTSTATUS status;
+
+ PAGED_CODE ();
+
+ NT_ASSERT ( Layout != NULL );
+
+
+ //
+ // Allocate enough space for a DRIVE_LAYOUT_INFORMATION_EX structure
+ // plus as many PARTITION_INFORMATION_EX structures as are in the
+ // source array.
+ //
+
+ //
+ // Use safe interger function
+ //
+
+ status = RtlULongMult(Layout->PartitionCount, sizeof(PARTITION_INFORMATION_EX), &size);
+ if (!NT_SUCCESS(status)) {
+ return NULL;
+ }
+ status = RtlULongAdd(size, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[0]),
+ &size);
+ if (!NT_SUCCESS(status)) {
+ return NULL;
+ }
+
+ layoutEx = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ size,
+ DISK_TAG_PART_LIST
+ );
+
+ if ( layoutEx == NULL ) {
+ return NULL;
+ }
+
+ //
+ // Convert the disk information.
+ //
+
+ layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
+ layoutEx->PartitionCount = Layout->PartitionCount;
+ layoutEx->Mbr.Signature = Layout->Signature;
+
+ for (i = 0; i < Layout->PartitionCount; i++) {
+
+ //
+ // Convert each entry.
+ //
+
+ DiskConvertPartitionToExtended (
+ &Layout->PartitionEntry[i],
+ &layoutEx->PartitionEntry[i]
+ );
+ }
+
+ return layoutEx;
+}
+
+
+NTSTATUS
+DiskEnumerateDevice(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the class driver to update the PDO list off
+ of this FDO. The disk driver also calls it internally to re-create
+ device objects.
+
+ This routine will read the partition table and create new PDO objects as
+ necessary. PDO's that no longer exist will be pulled out of the PDO list
+ so that pnp will destroy them.
+
+Arguments:
+
+ Fdo - a pointer to the FDO being re-enumerated
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
+ PDISK_DATA diskData = (PDISK_DATA) commonExtension->DriverData;
+ PDEVICE_OBJECT pdo = NULL;
+ PDRIVE_LAYOUT_INFORMATION_EX partitionList;
+ NTSTATUS status;
+ BOOLEAN freePartitionList = FALSE;
+
+ NT_ASSERT(commonExtension->IsFdo);
+
+ PAGED_CODE();
+
+ //
+ // If our cached partition table is valid, there is nothing to be done
+ //
+
+ if ( diskData->CachedPartitionTableValid == TRUE )
+ {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Update our image of the size of the drive. This may be necessary if
+ // the drive size is extended or we just released a reservation to
+ // ensure the kernel doesn't reject the partition table.
+ //
+
+ DiskReadDriveCapacity(Fdo);
+
+ //
+ // Lock out anyone else trying to repartition the disk.
+ //
+
+ DiskAcquirePartitioningLock(fdoExtension);
+
+ //
+ // Create objects for all the partitions on the device.
+ //
+
+ status = DiskReadPartitionTableEx(fdoExtension, FALSE, &partitionList);
+
+ //
+ // If the I/O read partition table failed and this is a removable device,
+ // then fix up the partition list to make it look like there is one
+ // zero length partition.
+ //
+
+ if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) &&
+ Fdo->Characteristics & FILE_REMOVABLE_MEDIA) {
+
+ SIZE_T partitionListSize;
+
+ //
+ // Remember whether the drive is ready.
+ //
+
+ diskData->ReadyStatus = status;
+
+ //
+ // Allocate and zero a partition list.
+ //
+
+ partitionListSize =
+ FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
+
+ partitionList = ExAllocatePoolWithTag(NonPagedPoolNx,
+ partitionListSize,
+ DISK_TAG_PART_LIST);
+
+ if (partitionList != NULL) {
+
+ RtlZeroMemory( partitionList, partitionListSize );
+
+ //
+ // Set the partition count to one and the status to success
+ // so one device object will be created. Set the partition type
+ // to a bogus value.
+ //
+
+ partitionList->PartitionStyle = PARTITION_STYLE_MBR;
+ partitionList->PartitionCount = 1;
+
+ freePartitionList = TRUE;
+
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ diskData->UpdatePartitionRoutine(Fdo, partitionList);
+ }
+
+ DiskReleasePartitioningLock(fdoExtension);
+
+ if (freePartitionList) {
+ FREE_POOL(partitionList);
+ }
+
+ return(STATUS_SUCCESS);
+
+} // end DiskEnumerateDevice()
+
+
+VOID
+DiskUpdateRemovablePartitions(
+ IN PDEVICE_OBJECT Fdo,
+ IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the class DLL to update the PDO list off of this
+ FDO. The disk driver also calls it internally to re-create device objects.
+
+ This routine will read the partition table and update the size of the
+ single partition device object which always exists for removable devices.
+
+Arguments:
+
+ Fdo - a pointer to the FDO being reenumerated.
+
+Return Value:
+
+ status
+
+--*/
+
+{
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
+
+ ULONG partitionCount;
+
+ ULONG partitionNumber;
+ ULONG partitionOrdinal = 0;
+ ULONG newPartitionNumber;
+
+ PDISK_DATA pdoData;
+ NTSTATUS status;
+
+ PPARTITION_INFORMATION_EX partitionEntry;
+ PARTITION_STYLE partitionStyle;
+
+ PAGED_CODE();
+
+ NT_ASSERT(Fdo->Characteristics & FILE_REMOVABLE_MEDIA);
+
+ partitionStyle = PartitionList->PartitionStyle;
+ partitionCount = PartitionList->PartitionCount;
+
+ for(partitionNumber = 0;
+ partitionNumber < partitionCount;
+ partitionNumber++) {
+
+ partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]);
+
+ partitionEntry->PartitionNumber = 0;
+ }
+
+ //
+ // Get exclusive access to the child list while repartitioning.
+ //
+
+ ClassAcquireChildLock(fdoExtension);
+
+ //
+ // Removable media should never have more than one PDO.
+ //
+
+ pdoExtension = fdoExtension->CommonExtension.ChildList;
+
+ if(pdoExtension == NULL) {
+
+ PARTITION_INFORMATION_EX tmpPartitionEntry = { 0 };
+ PDEVICE_OBJECT pdo;
+
+ //
+ // There is no PDO currently. Create one and pre-initialize it with
+ // a zero length.
+ //
+
+ tmpPartitionEntry.PartitionNumber = 1;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: Creating RM partition\n"));
+
+ status = DiskCreatePdo(Fdo,
+ 0,
+ &tmpPartitionEntry,
+ partitionStyle,
+ &pdo);
+
+ if(!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: error %lx creating "
+ "new PDO for RM partition\n",
+ status));
+
+ goto DiskUpdateRemovablePartitionsExit;
+ }
+
+ //
+ // mark the new device as enumerated
+ //
+
+ pdoExtension = pdo->DeviceExtension;
+ pdoExtension->IsMissing = FALSE;
+
+ }
+
+ pdoData = pdoExtension->CommonExtension.DriverData;
+
+ //
+ // Search the partition list for a valid entry. We're looking for a
+ // primary partition since we only support the one.
+ //
+
+ for(partitionNumber = 0;
+ partitionNumber < partitionCount;
+ partitionNumber++) {
+
+ partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]);
+
+
+ //
+ // Is this partition interesting?
+ //
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ if(partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
+ IsContainerPartition(partitionEntry->Mbr.PartitionType)) {
+
+ continue;
+ }
+ }
+
+ partitionOrdinal++;
+
+ //
+ // We have found the first and thus only partition allowed on
+ // this disk. Update the information in the PDO to match the new
+ // partition.
+ //
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdateRemovablePartitions: Matched %wZ to #%d, "
+ "ordinal %d\n",
+ &pdoExtension->CommonExtension.DeviceName,
+ partitionEntry->PartitionNumber,
+ partitionOrdinal));
+
+
+ partitionEntry->PartitionNumber = 1;
+
+ pdoData->PartitionStyle = partitionStyle;
+ pdoData->PartitionOrdinal = partitionOrdinal;
+ NT_ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789);
+
+ pdoExtension->CommonExtension.StartingOffset =
+ partitionEntry->StartingOffset;
+
+ pdoExtension->CommonExtension.PartitionLength =
+ partitionEntry->PartitionLength;
+
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors;
+ pdoData->Mbr.BootIndicator = partitionEntry->Mbr.BootIndicator;
+
+
+ //
+ // If this partition is being re-written then update the type
+ // information as well
+ //
+
+ if (partitionEntry->RewritePartition) {
+ pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType;
+ }
+
+ } else {
+
+ pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType;
+ pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId;
+ pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes;
+
+ RtlCopyMemory(
+ pdoData->Efi.PartitionName,
+ partitionEntry->Gpt.Name,
+ sizeof (pdoData->Efi.PartitionName)
+ );
+ }
+
+ //
+ // Mark this one as found
+ //
+
+ pdoExtension->IsMissing = FALSE;
+ goto DiskUpdateRemovablePartitionsExit;
+ }
+
+ //
+ // No interesting partition was found.
+ //
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ pdoData->Mbr.HiddenSectors = 0;
+ pdoData->Mbr.PartitionType = PARTITION_ENTRY_UNUSED;
+
+ } else {
+
+ RtlZeroMemory (&pdoData->Efi,
+ sizeof (pdoData->Efi)
+ );
+ }
+
+ pdoExtension->CommonExtension.StartingOffset.QuadPart = 0;
+ pdoExtension->CommonExtension.PartitionLength.QuadPart = 0;
+
+DiskUpdateRemovablePartitionsExit:
+
+ //
+ // Update the parent device object
+ //
+
+ {
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ if (partitionStyle == PARTITION_STYLE_MBR)
+ {
+ diskData->PartitionStyle = PARTITION_STYLE_MBR;
+ diskData->Mbr.Signature = PartitionList->Mbr.Signature;
+ }
+ else
+ {
+ diskData->PartitionStyle = PARTITION_STYLE_GPT;
+ diskData->Efi.DiskId = PartitionList->Gpt.DiskId;
+ }
+ }
+
+ ClassReleaseChildLock(fdoExtension);
+ return;
+}
+
+
+VOID
+DiskUpdatePartitions(
+ IN PDEVICE_OBJECT Fdo,
+ IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will synchronize the information held in the partition list
+ with the device objects hanging off this Fdo. Any new partition objects
+ will be created, any non-existant ones will be marked as un-enumerated.
+
+ This will be done in several stages:
+
+ * Clear state (partition number) from every entry in the partition
+ list
+
+ * Set IsMissing flag on every child of this FDO
+
+ * For each child of the FDO:
+ if a matching partition exists in the partition list,
+ update the partition number in the table, update the
+ ordinal in the object and mark the object as enumerated
+
+ * For each un-enumerated device object
+ zero out the partition information to invalidate the device
+ delete the symbolic link if any
+
+ * For each un-matched entry in the partition list:
+ create a new partition object
+ update the partition number in the list entry
+ create a new symbolic link if necessary
+
+Arguments:
+
+ Fdo - a pointer to the functional device object this partition list is for
+
+ PartitionList - a pointer to the partition list being updated
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PPHYSICAL_DEVICE_EXTENSION oldChildList = NULL;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
+
+ ULONG partitionCount;
+
+ ULONG partitionNumber;
+ ULONG partitionOrdinal;
+ ULONG newPartitionNumber;
+
+ PPARTITION_INFORMATION_EX partitionEntry;
+ PDISK_DATA pdoData;
+ PARTITION_STYLE partitionStyle;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Get exclusive access to the child list.
+ //
+
+ ClassAcquireChildLock(fdoExtension);
+
+ partitionStyle = PartitionList->PartitionStyle;
+ partitionCount = PartitionList->PartitionCount;
+
+ //
+ // Pull all the child device objects off the children list. We'll
+ // add them back later.
+ //
+
+ oldChildList = fdoExtension->CommonExtension.ChildList;
+ fdoExtension->CommonExtension.ChildList = NULL;
+
+ //
+ // Clear the partition numbers from the list entries
+ //
+
+ for(partitionNumber = 0;
+ partitionNumber < partitionCount;
+ partitionNumber++) {
+
+ partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]);
+ partitionEntry->PartitionNumber = 0;
+ }
+
+ //
+ // Now match each child partition to it's entry (if any) in the partition
+ // list.
+ //
+
+ while(oldChildList != NULL) {
+
+ pdoExtension = oldChildList;
+ pdoData = pdoExtension->CommonExtension.DriverData;
+
+ //
+ // Check all partition entries for a match on offset and length
+ //
+
+ partitionOrdinal = 0;
+
+ for(partitionNumber = 0;
+ partitionNumber < partitionCount;
+ partitionNumber++) {
+
+ partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]);
+
+ //
+ // Is this an interesting partition entry?
+ //
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) ||
+ (IsContainerPartition(partitionEntry->Mbr.PartitionType))) {
+
+ continue;
+ }
+ }
+
+ partitionOrdinal++;
+
+ if(partitionEntry->PartitionNumber) {
+
+ //
+ // This partition has already been found - skip it
+ //
+
+ continue;
+ }
+
+ //
+ // Let's see if the partition information matches
+ //
+
+ if(partitionEntry->StartingOffset.QuadPart !=
+ pdoExtension->CommonExtension.StartingOffset.QuadPart) {
+ continue;
+ }
+
+ if(partitionEntry->PartitionLength.QuadPart !=
+ pdoExtension->CommonExtension.PartitionLength.QuadPart) {
+ continue;
+ }
+
+ //
+ // Yep - it matches. Update the information in the entry
+ //
+
+ partitionEntry->PartitionNumber = pdoExtension->CommonExtension.PartitionNumber;
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ pdoData->Mbr.HiddenSectors = partitionEntry->Mbr.HiddenSectors;
+
+ }
+
+ break;
+ }
+
+ if(partitionNumber != partitionCount) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Matched %wZ to #%d, ordinal "
+ "%d\n",
+ &pdoExtension->CommonExtension.DeviceName,
+ partitionEntry->PartitionNumber,
+ partitionOrdinal));
+
+ NT_ASSERT(partitionEntry->PartitionLength.LowPart != 0x23456789);
+ // NT_ASSERT(pdoExtension->CommonExtension.PartitionLength.QuadPart != 0);
+
+ pdoData->PartitionStyle = partitionStyle;
+
+ //
+ // we found a match - update the information in the device object
+ // extension and driverdata
+ //
+
+ pdoData->PartitionOrdinal = partitionOrdinal;
+
+ //
+ // If this partition is being re-written then update the type
+ // information as well
+ //
+
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ if(partitionEntry->RewritePartition) {
+ pdoData->Mbr.PartitionType = partitionEntry->Mbr.PartitionType;
+ }
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: EFI Partition %ws\n",
+ pdoData->Efi.PartitionName
+ ));
+
+ pdoData->Efi.PartitionType = partitionEntry->Gpt.PartitionType;
+ pdoData->Efi.PartitionId = partitionEntry->Gpt.PartitionId;
+ pdoData->Efi.Attributes = partitionEntry->Gpt.Attributes;
+
+ RtlCopyMemory(
+ pdoData->Efi.PartitionName,
+ partitionEntry->Gpt.Name,
+ sizeof (pdoData->Efi.PartitionName)
+ );
+ }
+
+ //
+ // Mark this one as found.
+ //
+
+ pdoExtension->IsMissing = FALSE;
+
+ //
+ // Pull it out of the old child list and add it into the
+ // real one.
+ //
+
+ oldChildList = pdoExtension->CommonExtension.ChildList;
+
+ pdoExtension->CommonExtension.ChildList =
+ fdoExtension->CommonExtension.ChildList;
+
+ fdoExtension->CommonExtension.ChildList = pdoExtension;
+
+ } else {
+
+ PDEVICE_OBJECT nextPdo;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Deleting %wZ\n",
+ &pdoExtension->CommonExtension.DeviceName));
+
+ if (partitionStyle == PARTITION_STYLE_GPT) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: EFI Partition %ws\n",
+ pdoData->Efi.PartitionName
+ ));
+ }
+ //
+ // no matching entry in the partition list - throw this partition
+ // object away
+ //
+
+ pdoExtension->CommonExtension.PartitionLength.QuadPart = 0;
+
+ //
+ // grab a pointer to the next child before we mark this one as
+ // missing since missing devices could vanish at any time.
+ //
+
+ oldChildList = pdoExtension->CommonExtension.ChildList;
+ pdoExtension->CommonExtension.ChildList = (PVOID) -1;
+
+ //
+ // Now tell the class driver that this child is "missing" - this
+ // will cause it to be deleted.
+ //
+
+
+ ClassMarkChildMissing(pdoExtension, FALSE);
+ }
+ }
+
+ //
+ // At this point the old child list had best be empty.
+ //
+
+ NT_ASSERT(oldChildList == NULL);
+
+ //
+ // Iterate through the partition entries and create any partition
+ // objects that don't already exist
+ //
+
+ partitionOrdinal = 0;
+ newPartitionNumber = 0;
+
+ for(partitionNumber = 0;
+ partitionNumber < partitionCount;
+ partitionNumber++) {
+
+ PDEVICE_OBJECT pdo;
+
+ partitionEntry = &(PartitionList->PartitionEntry[partitionNumber]);
+
+ //
+ // Is this partition interesting
+ //
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+
+ if((partitionEntry->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) ||
+ (IsContainerPartition(partitionEntry->Mbr.PartitionType))) {
+
+ continue;
+ }
+ }
+
+ //
+ // Increment the count of interesting partitions
+ //
+
+ partitionOrdinal++;
+ newPartitionNumber++;
+
+ //
+ // Has this already been matched
+ //
+
+ if(partitionEntry->PartitionNumber == 0) {
+
+ LONG i;
+
+ //
+ // find the first safe partition number for this device
+ //
+
+ for(i = 0; i < (LONG) partitionCount; i++) {
+
+
+ PPARTITION_INFORMATION_EX tmp = &(PartitionList->PartitionEntry[i]);
+
+ if (partitionStyle == PARTITION_STYLE_MBR) {
+ if (tmp->Mbr.PartitionType == PARTITION_ENTRY_UNUSED ||
+ IsContainerPartition(tmp->Mbr.PartitionType)) {
+ continue;
+ }
+ }
+
+ if(tmp->PartitionNumber == newPartitionNumber) {
+
+ //
+ // Found a matching partition number - increment the count
+ // and restart the scan.
+ //
+
+ newPartitionNumber++;
+ i = -1;
+ continue;
+ }
+ }
+
+ //
+ // Assign this partition a partition number
+ //
+
+ partitionEntry->PartitionNumber = newPartitionNumber;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskUpdatePartitions: Found new partition #%d, ord %d "
+ "starting at %#016I64x and running for %#016I64x\n",
+ partitionEntry->PartitionNumber,
+ partitionOrdinal,
+ partitionEntry->StartingOffset.QuadPart,
+ partitionEntry->PartitionLength.QuadPart));
+
+ ClassReleaseChildLock(fdoExtension);
+
+ status = DiskCreatePdo(Fdo,
+ partitionOrdinal,
+ partitionEntry,
+ partitionStyle,
+ &pdo);
+
+ ClassAcquireChildLock(fdoExtension);
+
+ if(!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskUpdatePartitions: error %lx creating "
+ "new PDO for partition ordinal %d, number %d\n",
+ status,
+ partitionOrdinal,
+ partitionEntry->PartitionNumber));
+
+ //
+ // don't increment the partition number - we'll try to reuse
+ // it for the next child.
+ //
+
+ partitionEntry->PartitionNumber = 0;
+ newPartitionNumber--;
+
+ continue;
+ }
+
+ //
+ // mark the new device as enumerated
+ //
+
+ pdoExtension = pdo->DeviceExtension;
+ pdoExtension->IsMissing = FALSE;
+
+ //
+ // This number's taken already - try to scanning the partition
+ // table more than once for a new number.
+ //
+
+ }
+ }
+
+ //
+ // Update the parent device object
+ //
+
+ {
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ if (partitionStyle == PARTITION_STYLE_MBR)
+ {
+ diskData->PartitionStyle = PARTITION_STYLE_MBR;
+ diskData->Mbr.Signature = PartitionList->Mbr.Signature;
+ }
+ else
+ {
+ diskData->PartitionStyle = PARTITION_STYLE_GPT;
+ diskData->Efi.DiskId = PartitionList->Gpt.DiskId;
+ }
+ }
+
+ ClassReleaseChildLock(fdoExtension);
+ return;
+}
+
+
+NTSTATUS
+DiskCreatePdo(
+ IN PDEVICE_OBJECT Fdo,
+ IN ULONG PartitionOrdinal,
+ IN PPARTITION_INFORMATION_EX PartitionEntry,
+ IN PARTITION_STYLE PartitionStyle,
+ OUT PDEVICE_OBJECT *Pdo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will create and initialize a new partition device object
+ (PDO) and insert it into the FDO partition list.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object this PDO will be a child
+ of
+
+ PartitionOrdinal - the partition ordinal for this PDO
+
+ PartitionEntry - the partition information for this device object
+
+ PartitionStyle - what style of partition table entry PartitionEntry is;
+ currently either MBR or EFI
+
+ Pdo - a location to store the pdo pointer upon successful completion
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PDEVICE_OBJECT pdo = NULL;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
+
+ PUCHAR deviceName = NULL;
+
+ PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
+
+ ULONG numberListElements;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // Create partition object and set up partition parameters.
+ //
+
+ status = DiskGenerateDeviceName(FALSE,
+ fdoExtension->DeviceNumber,
+ PartitionEntry->PartitionNumber,
+ &PartitionEntry->StartingOffset,
+ &PartitionEntry->PartitionLength,
+ &deviceName);
+
+ if(!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskCreatePdo - Can't generate name %lx\n", status));
+ return status;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskCreatePdo: Create device object %s\n", deviceName));
+
+ status = ClassCreateDeviceObject(Fdo->DriverObject,
+ deviceName,
+ Fdo,
+ FALSE,
+ &pdo);
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM, "DiskEnumerateDevice: Can't create device object for %s\n", deviceName));
+
+ FREE_POOL(deviceName);
+ return status;
+ }
+
+ FREE_POOL(deviceName);
+
+ //
+ // Set up device extension fields.
+ //
+
+ pdoExtension = pdo->DeviceExtension;
+
+ //
+ // Set up device object fields.
+ //
+
+ SET_FLAG(pdo->Flags, DO_DIRECT_IO);
+
+ pdo->StackSize = (CCHAR)
+ pdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1;
+
+ //
+ // Get pointer to new disk data.
+ //
+
+ diskData = (PDISK_DATA) pdoExtension->CommonExtension.DriverData;
+
+ //
+ // Set the alignment requirements for the device based on the
+ // host adapter requirements
+ //
+
+ if (Fdo->AlignmentRequirement > pdo->AlignmentRequirement) {
+ pdo->AlignmentRequirement = Fdo->AlignmentRequirement;
+ }
+
+ if (fdoExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
+ numberListElements = 30;
+ } else {
+ numberListElements = 8;
+ }
+
+ //
+ // Build the lookaside list for srb's for this partition based on
+ // whether the adapter and disk can do tagged queueing. Don't bother to
+ // check the status - this can't fail when called for a PDO.
+ //
+
+ ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) pdoExtension,
+ numberListElements);
+
+ //
+ // Set the sense-data pointer in the device extension.
+ //
+
+ diskData->PartitionOrdinal = PartitionOrdinal;
+ pdoExtension->CommonExtension.PartitionNumber = PartitionEntry->PartitionNumber;
+
+ //
+ // Initialize relevant data.
+ //
+
+ diskData->PartitionStyle = PartitionStyle;
+
+ if (PartitionStyle == PARTITION_STYLE_MBR) {
+
+ diskData->Mbr.PartitionType = PartitionEntry->Mbr.PartitionType;
+ diskData->Mbr.BootIndicator = PartitionEntry->Mbr.BootIndicator;
+ diskData->Mbr.HiddenSectors = PartitionEntry->Mbr.HiddenSectors;
+
+ } else {
+
+ diskData->Efi.PartitionType = PartitionEntry->Gpt.PartitionType;
+ diskData->Efi.PartitionId = PartitionEntry->Gpt.PartitionId;
+ diskData->Efi.Attributes = PartitionEntry->Gpt.Attributes;
+ RtlCopyMemory (diskData->Efi.PartitionName,
+ PartitionEntry->Gpt.Name,
+ sizeof (diskData->Efi.PartitionName)
+ );
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskEnumerateDevice: Partition type is %x\n",
+ diskData->Mbr.PartitionType));
+
+ pdoExtension->CommonExtension.StartingOffset =
+ PartitionEntry->StartingOffset;
+
+ pdoExtension->CommonExtension.PartitionLength =
+ PartitionEntry->PartitionLength;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_ENUM, "DiskCreatePdo: hidden sectors value for pdo %#p set to %#x\n",
+ pdo,
+ diskData->Mbr.HiddenSectors));
+
+ //
+ // Check for removable media support.
+ //
+
+ if (fdoExtension->DeviceDescriptor->RemovableMedia) {
+ SET_FLAG(pdo->Characteristics, FILE_REMOVABLE_MEDIA);
+ }
+
+ pdoExtension->CommonExtension.DeviceObject = pdo;
+
+ CLEAR_FLAG(pdo->Flags, DO_DEVICE_INITIALIZING);
+
+ *Pdo = pdo;
+
+ return status;
+}
+
+
+/*
+ * DiskAcquirePartitioningLock
+ *
+ * Acquire the PartitioningEvent.
+ *
+ * NOTE: This function is called by several ioctl handlers which run in user context.
+ * Because we are acquiring an exclusion object in a user thread, we have to make sure
+ * that the thread is not killed or suspended while we are holding the event.
+ * So we call KeEnterCriticalRegion/KeLeaveCriticalRegion while holding the PartitioningEvent.
+ * THEREFORE, it is VERY IMPORTANT that DiskAcquirePartitioningLock and DiskReleasePartitioningLock
+ * are called on the SAME THREAD.
+ */
+VOID
+DiskAcquirePartitioningLock(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+
+ PAGED_CODE();
+
+ ASSERT_FDO(FdoExtension->DeviceObject);
+
+ /*
+ * Don't let user-mode thread get suspended while we are holding the partitioning lock
+ */
+ KeEnterCriticalRegion();
+
+ KeWaitForSingleObject(&(diskData->PartitioningEvent),
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+ return;
+}
+
+
+VOID
+DiskReleasePartitioningLock(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+
+ PAGED_CODE();
+
+ ASSERT_FDO(FdoExtension->DeviceObject);
+
+ KeSetEvent(&(diskData->PartitioningEvent), IO_NO_INCREMENT, FALSE);
+
+ KeLeaveCriticalRegion();
+
+ return;
+}
diff --git a/storage/class/disk/src/geometry.c b/storage/class/disk/src/geometry.c
new file mode 100644
index 00000000..911a33a4
--- /dev/null
+++ b/storage/class/disk/src/geometry.c
@@ -0,0 +1,1672 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ geometry.c
+
+Abstract:
+
+ SCSI disk class driver - this module contains all the code for generating
+ disk geometries.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+
+#include "disk.h"
+#include "ntddstor.h"
+
+#ifdef DEBUG_USE_WPP
+#include "geometry.tmh"
+#endif
+
+#if defined(_X86_) || defined(_AMD64_)
+
+DISK_GEOMETRY_SOURCE
+DiskUpdateGeometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension
+ );
+
+NTSTATUS
+DiskUpdateRemovableGeometry (
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+DiskScanBusDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE BusKey
+ );
+
+NTSTATUS
+DiskSaveBusDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE TargetKey,
+ IN ULONG DiskNumber
+ );
+
+NTSTATUS
+DiskSaveGeometryDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE HardwareKey
+ );
+
+NTSTATUS
+DiskGetPortGeometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PDISK_GEOMETRY Geometry
+ );
+
+typedef struct _DISK_DETECT_INFO {
+ BOOLEAN Initialized;
+ ULONG Style;
+ ULONG Signature;
+ ULONG MbrCheckSum;
+ PDEVICE_OBJECT Device;
+ CM_INT13_DRIVE_PARAMETER DriveParameters;
+} DISK_DETECT_INFO, *PDISK_DETECT_INFO;
+
+//
+// Information about the disk geometries collected and saved into the registry
+// by NTDETECT.COM or the system firmware.
+//
+
+PDISK_DETECT_INFO DetectInfoList = NULL;
+ULONG DetectInfoCount = 0;
+LONG DetectInfoUsedCount = 0;
+
+#define GET_STARTING_SECTOR(p) ( \
+ (ULONG) (p->StartingSectorLsb0) + \
+ (ULONG) (p->StartingSectorLsb1 << 8 ) + \
+ (ULONG) (p->StartingSectorMsb0 << 16) + \
+ (ULONG) (p->StartingSectorMsb1 << 24) )
+
+#define GET_ENDING_S_OF_CHS(p) ( \
+ (UCHAR) (p->EndingCylinderLsb & 0x3F) )
+
+//
+// Definitions from hal.h
+//
+
+//
+// Boot record disk partition table entry structure format
+//
+
+typedef struct _PARTITION_DESCRIPTOR
+{
+ UCHAR ActiveFlag;
+ UCHAR StartingTrack;
+ UCHAR StartingCylinderLsb;
+ UCHAR StartingCylinderMsb;
+ UCHAR PartitionType;
+ UCHAR EndingTrack;
+ UCHAR EndingCylinderLsb;
+ UCHAR EndingCylinderMsb;
+ UCHAR StartingSectorLsb0;
+ UCHAR StartingSectorLsb1;
+ UCHAR StartingSectorMsb0;
+ UCHAR StartingSectorMsb1;
+ UCHAR PartitionLengthLsb0;
+ UCHAR PartitionLengthLsb1;
+ UCHAR PartitionLengthMsb0;
+ UCHAR PartitionLengthMsb1;
+
+} PARTITION_DESCRIPTOR, *PPARTITION_DESCRIPTOR;
+
+//
+// Number of partition table entries
+//
+
+#define NUM_PARTITION_TABLE_ENTRIES 4
+
+//
+// Partition table record and boot signature offsets in 16-bit words
+//
+
+#define PARTITION_TABLE_OFFSET ( 0x1be / 2)
+#define BOOT_SIGNATURE_OFFSET ((0x200 / 2) - 1)
+
+//
+// Boot record signature value
+//
+
+#define BOOT_RECORD_SIGNATURE (0xaa55)
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT, DiskSaveDetectInfo)
+#pragma alloc_text(INIT, DiskScanBusDetectInfo)
+#pragma alloc_text(INIT, DiskSaveBusDetectInfo)
+#pragma alloc_text(INIT, DiskSaveGeometryDetectInfo)
+
+#pragma alloc_text(PAGE, DiskUpdateGeometry)
+#pragma alloc_text(PAGE, DiskUpdateRemovableGeometry)
+#pragma alloc_text(PAGE, DiskGetPortGeometry)
+#pragma alloc_text(PAGE, DiskIsNT4Geometry)
+#pragma alloc_text(PAGE, DiskGetDetectInfo)
+#pragma alloc_text(PAGE, DiskReadSignature)
+#endif
+
+
+NTSTATUS
+DiskSaveDetectInfo(
+ PDRIVER_OBJECT DriverObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine saves away the firmware information about the disks which has
+ been saved in the registry. It generates a list (DetectInfoList) which
+ contains the disk geometries, signatures & checksums of all drives which
+ were examined by NtDetect. This list is later used to assign geometries
+ to disks as they are initialized.
+
+Arguments:
+
+ DriverObject - the driver being initialized. This is used to get to the
+ hardware database.
+
+Return Value:
+
+ status.
+
+--*/
+
+{
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ HANDLE hardwareKey;
+
+ UNICODE_STRING unicodeString;
+ HANDLE busKey;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ DriverObject->HardwareDatabase,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ //
+ // Create the hardware base key.
+ //
+
+ status = ZwOpenKey(&hardwareKey, KEY_READ, &objectAttributes);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Cannot open hardware data. "
+ "Name: %wZ\n",
+ DriverObject->HardwareDatabase));
+ return status;
+ }
+
+ status = DiskSaveGeometryDetectInfo(DriverObject, hardwareKey);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Can't query configuration data "
+ "(%#08lx)\n",
+ status));
+ ZwClose(hardwareKey);
+ return status;
+ }
+
+ //
+ // Open EISA bus key.
+ //
+
+ RtlInitUnicodeString(&unicodeString, L"EisaAdapter");
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ hardwareKey,
+ NULL);
+
+ status = ZwOpenKey(&busKey,
+ KEY_READ,
+ &objectAttributes);
+
+ if(NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Opened EisaAdapter key\n"));
+ DiskScanBusDetectInfo(DriverObject, busKey);
+ ZwClose(busKey);
+ }
+
+ //
+ // Open MultiFunction bus key.
+ //
+
+ RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter");
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ hardwareKey,
+ NULL);
+
+ status = ZwOpenKey(&busKey,
+ KEY_READ,
+ &objectAttributes);
+
+ if(NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n"));
+ DiskScanBusDetectInfo(DriverObject, busKey);
+ ZwClose(busKey);
+ }
+
+ ZwClose(hardwareKey);
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DiskCleanupDetectInfo(
+ IN PDRIVER_OBJECT DriverObject
+ )
+/*++
+
+Routine Description:
+
+ This routine will cleanup the data structure built by DiskSaveDetectInfo.
+
+Arguments:
+
+ DriverObject - a pointer to the kernel object for this driver.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(DriverObject);
+ FREE_POOL(DetectInfoList);
+ return;
+}
+
+
+NTSTATUS
+DiskSaveGeometryDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE HardwareKey
+ )
+{
+ UNICODE_STRING unicodeString;
+ PKEY_VALUE_FULL_INFORMATION keyData;
+ ULONG length;
+
+ PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
+
+ PCM_INT13_DRIVE_PARAMETER driveParameters;
+ ULONG numberOfDrives;
+
+ ULONG i;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ //
+ // Get disk BIOS geometry information.
+ //
+
+ RtlInitUnicodeString(&unicodeString, L"Configuration Data");
+
+ keyData = ExAllocatePoolWithTag(PagedPool,
+ VALUE_BUFFER_SIZE,
+ DISK_TAG_UPDATE_GEOM);
+
+ if(keyData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Can't allocate config "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = ZwQueryValueKey(HardwareKey,
+ &unicodeString,
+ KeyValueFullInformation,
+ keyData,
+ VALUE_BUFFER_SIZE,
+ &length);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Can't query configuration "
+ "data (%#08lx)\n",
+ status));
+ FREE_POOL(keyData);
+ return status;
+ }
+
+ //
+ // Extract the resource list out of the key data.
+ //
+
+ fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
+ (((PUCHAR) keyData) + keyData->DataOffset);
+ partialDescriptor =
+ fullDescriptor->PartialResourceList.PartialDescriptors;
+ length = partialDescriptor->u.DeviceSpecificData.DataSize;
+
+ if((keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) ||
+ (fullDescriptor->PartialResourceList.Count == 0) ||
+ (partialDescriptor->Type != CmResourceTypeDeviceSpecific) ||
+ (length < sizeof(ULONG))) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: BIOS header data too small "
+ "or invalid\n"));
+ FREE_POOL(keyData);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Point to the BIOS data. THe BIOS data is located after the first
+ // partial Resource list which should be device specific data.
+ //
+
+ {
+ PUCHAR buffer = (PUCHAR) keyData;
+ buffer += keyData->DataOffset;
+ buffer += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
+ driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer;
+ }
+
+ numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER);
+
+ //
+ // Allocate our detect info list now that we know how many entries there
+ // are going to be. No other routine allocates detect info and this is
+ // done out of DriverEntry so we don't need to synchronize it's creation.
+ //
+
+ length = sizeof(DISK_DETECT_INFO) * numberOfDrives;
+ DetectInfoList = ExAllocatePoolWithTag(PagedPool,
+ length,
+ DISK_TAG_UPDATE_GEOM);
+
+ if(DetectInfoList == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes "
+ "for DetectInfoList\n",
+ length));
+
+ FREE_POOL(keyData);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ DetectInfoCount = numberOfDrives;
+
+ RtlZeroMemory(DetectInfoList, length);
+
+ //
+ // Copy the information out of the key data and into the list we've
+ // allocated.
+ //
+
+ for(i = 0; i < numberOfDrives; i++) {
+#pragma warning(suppress: 6386) // PREFast bug means it doesn't correctly remember the size of DetectInfoList
+ DetectInfoList[i].DriveParameters = driveParameters[i];
+ }
+
+ FREE_POOL(keyData);
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DiskScanBusDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE BusKey
+ )
+/*++
+
+Routine Description:
+
+ The routine queries the registry to determine which disks are visible to
+ the BIOS. If a disk is visable to the BIOS then the geometry information
+ is updated with the disk's signature and MBR checksum.
+
+Arguments:
+
+ DriverObject - the object for this driver.
+ BusKey - handle to the bus key to be enumerated.
+
+Return Value:
+
+ status
+
+--*/
+{
+ ULONG busNumber;
+
+ NTSTATUS status;
+
+ for(busNumber = 0; ; busNumber++) {
+
+ WCHAR buffer[32] = { 0 };
+ UNICODE_STRING unicodeString;
+
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+
+ HANDLE spareKey;
+ HANDLE adapterKey;
+
+ ULONG adapterNumber;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber));
+
+ //
+ // Open controller name key.
+ //
+
+ status = RtlStringCchPrintfW(buffer, sizeof(buffer) / sizeof(buffer[0]) - 1, L"%d", busNumber);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status));
+ break;
+ }
+
+ RtlInitUnicodeString(&unicodeString, buffer);
+
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ BusKey,
+ NULL);
+
+ status = ZwOpenKey(&spareKey, KEY_READ, &objectAttributes);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening bus "
+ "key %#x\n",
+ status, busNumber));
+ break;
+ }
+
+ //
+ // Open up a controller ordinal key.
+ //
+
+ RtlInitUnicodeString(&unicodeString, L"DiskController");
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ spareKey,
+ NULL);
+
+ status = ZwOpenKey(&adapterKey, KEY_READ, &objectAttributes);
+ ZwClose(spareKey);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening "
+ "DiskController key\n",
+ status));
+ continue;
+ }
+
+ for(adapterNumber = 0; ; adapterNumber++) {
+
+ HANDLE diskKey;
+ ULONG diskNumber;
+
+ //
+ // Open disk key.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning disk key "
+ "%d\\DiskController\\%d\\DiskPeripheral\n",
+ busNumber, adapterNumber));
+
+ status = RtlStringCchPrintfW(buffer, sizeof(buffer) / sizeof(buffer[0]) - 1, L"%d\\DiskPeripheral", adapterNumber);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status));
+ break;
+ }
+
+ RtlInitUnicodeString(&unicodeString, buffer);
+
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ adapterKey,
+ NULL);
+
+ status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening "
+ "disk key\n",
+ status));
+ break;
+ }
+
+ for(diskNumber = 0; ; diskNumber++) {
+
+ HANDLE targetKey;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning target key "
+ "%d\\DiskController\\%d\\DiskPeripheral\\%d\n",
+ busNumber, adapterNumber, diskNumber));
+
+ status = RtlStringCchPrintfW(buffer, sizeof(buffer) / sizeof(buffer[0]) - 1, L"%d", diskNumber);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status));
+ break;
+ }
+
+ RtlInitUnicodeString(&unicodeString, buffer);
+
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeString,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ diskKey,
+ NULL);
+
+ status = ZwOpenKey(&targetKey, KEY_READ, &objectAttributes);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx "
+ "opening target key\n",
+ status));
+ break;
+ }
+
+ DiskSaveBusDetectInfo(DriverObject, targetKey, diskNumber);
+
+ ZwClose(targetKey);
+ }
+
+ ZwClose(diskKey);
+ }
+
+ ZwClose(adapterKey);
+ }
+
+ return;
+}
+
+
+NTSTATUS
+DiskSaveBusDetectInfo(
+ IN PDRIVER_OBJECT DriverObject,
+ IN HANDLE TargetKey,
+ IN ULONG DiskNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine will transfer the firmware/ntdetect reported information
+ in the specified target key into the appropriate entry in the
+ DetectInfoList.
+
+Arguments:
+
+ DriverObject - the object for this driver.
+
+ TargetKey - the key for the disk being saved.
+
+ DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this
+ entry
+
+Return Value:
+
+ status
+
+--*/
+{
+ PDISK_DETECT_INFO diskInfo;
+
+ UNICODE_STRING unicodeString;
+
+ PKEY_VALUE_FULL_INFORMATION keyData;
+ ULONG length;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ if (DiskNumber >= DetectInfoCount)
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ diskInfo = &(DetectInfoList[DiskNumber]);
+
+ if(diskInfo->Initialized) {
+
+ NT_ASSERT(FALSE);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: disk entry %#x already has a "
+ "signature of %#08lx and mbr checksum of %#08lx\n",
+ DiskNumber,
+ diskInfo->Signature,
+ diskInfo->MbrCheckSum));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ RtlInitUnicodeString(&unicodeString, L"Identifier");
+
+ keyData = ExAllocatePoolWithTag(PagedPool,
+ VALUE_BUFFER_SIZE,
+ DISK_TAG_UPDATE_GEOM);
+
+ if(keyData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Couldn't allocate space for "
+ "registry data\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Get disk peripheral identifier.
+ //
+
+ status = ZwQueryValueKey(TargetKey,
+ &unicodeString,
+ KeyValueFullInformation,
+ keyData,
+ VALUE_BUFFER_SIZE,
+ &length);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx getting "
+ "Identifier\n",
+ status));
+ FREE_POOL(keyData);
+ return status;
+
+ } else if (keyData->DataLength < 9*sizeof(WCHAR)) {
+
+ //
+ // the data is too short to use (we subtract 9 chars in normal path)
+ //
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Saved data was invalid, "
+ "not enough data in registry!\n"));
+ FREE_POOL(keyData);
+ return STATUS_UNSUCCESSFUL;
+
+ } else {
+
+ UNICODE_STRING identifier;
+ ULONG value;
+
+ //
+ // Complete unicode string.
+ //
+
+ identifier.Buffer = (PWSTR) ((PUCHAR)keyData + keyData->DataOffset);
+ identifier.Length = (USHORT) keyData->DataLength;
+ identifier.MaximumLength = (USHORT) keyData->DataLength;
+
+ //
+ // Get the first value out of the identifier - this will be the MBR
+ // checksum.
+ //
+
+ status = RtlUnicodeStringToInteger(&identifier, 16, &value);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx converting "
+ "identifier %wZ into MBR xsum\n",
+ status,
+ &identifier));
+ FREE_POOL(keyData);
+ return status;
+ }
+
+ diskInfo->MbrCheckSum = value;
+
+ //
+ // Shift the string over to get the disk signature
+ //
+
+ identifier.Buffer += 9;
+ identifier.Length -= 9 * sizeof(WCHAR);
+ identifier.MaximumLength -= 9 * sizeof(WCHAR);
+
+ status = RtlUnicodeStringToInteger(&identifier, 16, &value);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx converting "
+ "identifier %wZ into disk signature\n",
+ status,
+ &identifier));
+ value = 0;
+ }
+
+ diskInfo->Signature = value;
+ }
+
+ //
+ // Here is where we would save away the extended int13 data.
+ //
+
+ //
+ // Mark this entry as initialized so we can make sure not to do it again.
+ //
+
+ diskInfo->Initialized = TRUE;
+
+ FREE_POOL(keyData);
+
+ return STATUS_SUCCESS;
+}
+
+
+DISK_GEOMETRY_SOURCE
+DiskUpdateGeometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+/*++
+
+Routine Description:
+
+ This routine checks the DetectInfoList saved away during disk driver init
+ to see if any geometry information was reported for this drive. If the
+ geometry data exists (determined by matching non-zero signatures or
+ non-zero MBR checksums) then it will be saved in the RealGeometry member
+ of the disk data block.
+
+ ClassReadDriveCapacity MUST be called after calling this routine to update
+ the cylinder count based on the size of the disk and the presence of any
+ disk management software.
+
+Arguments:
+
+ DeviceExtension - Supplies a pointer to the device information for disk.
+
+Return Value:
+
+ Inidicates whether the "RealGeometry" in the data block is now valid.
+
+--*/
+
+{
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+
+ ULONG i;
+ PDISK_DETECT_INFO diskInfo = NULL;
+
+ BOOLEAN found = FALSE;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+
+ NT_ASSERT((FdoExtension->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0);
+
+ //
+ // If we've already set a non-default geometry for this drive then there's
+ // no need to try and update again.
+ //
+
+ if(diskData->GeometrySource != DiskGeometryUnknown) {
+ return diskData->GeometrySource;
+ }
+
+ //
+ // Scan through the saved detect info to see if we can find a match
+ // for this device.
+ //
+
+ for(i = 0; i < DetectInfoCount; i++) {
+
+ NT_ASSERT(DetectInfoList != NULL);
+
+ diskInfo = &(DetectInfoList[i]);
+
+ if((diskData->Mbr.Signature != 0) &&
+ (diskData->Mbr.Signature == diskInfo->Signature)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: found match for signature "
+ "%#08lx\n",
+ diskData->Mbr.Signature));
+ found = TRUE;
+ break;
+ } else if((diskData->Mbr.Signature == 0) &&
+ (diskData->Mbr.MbrCheckSum != 0) &&
+ (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: found match for xsum %#08lx\n",
+ diskData->Mbr.MbrCheckSum));
+ found = TRUE;
+ break;
+ }
+ }
+
+ if(found) {
+
+ ULONG cylinders;
+ ULONG sectorsPerTrack;
+ ULONG tracksPerCylinder;
+
+ ULONG length;
+
+ //
+ // Point to the array of drive parameters.
+ //
+
+ cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
+ sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
+ tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
+
+ //
+ // Since the BIOS may not report the full drive, recalculate the drive
+ // size based on the volume size and the BIOS values for tracks per
+ // cylinder and sectors per track..
+ //
+
+ length = tracksPerCylinder * sectorsPerTrack;
+
+ if (length == 0) {
+
+ //
+ // The BIOS information is bogus.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n",
+ tracksPerCylinder, sectorsPerTrack));
+ return DiskGeometryUnknown;
+ }
+
+ //
+ // since we are copying the structure RealGeometry here, we should
+ // really initialize all the fields, especially since a zero'd
+ // BytesPerSector field would cause a trap in xHalReadPartitionTable()
+ //
+
+ diskData->RealGeometry = FdoExtension->DiskGeometry;
+
+ //
+ // Save the geometry information away in the disk data block and
+ // set the bit indicating that we found a valid one.
+ //
+
+ diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
+ diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
+ diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, "
+ "#cylinders %#x\n",
+ sectorsPerTrack, tracksPerCylinder, cylinders));
+
+ diskData->GeometrySource = DiskGeometryFromBios;
+ diskInfo->Device = FdoExtension->DeviceObject;
+
+ //
+ // Increment the count of used geometry entries.
+ //
+
+ InterlockedIncrement(&DetectInfoUsedCount);
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData->Mbr.Signature));
+ }
+
+ if(diskData->GeometrySource == DiskGeometryUnknown) {
+
+ //
+ // We couldn't find a geometry from the BIOS. Check with the port
+ // driver and see if it can provide one.
+ //
+
+ status = DiskGetPortGeometry(FdoExtension, &(diskData->RealGeometry));
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // Check the geometry to make sure it's valid.
+ //
+
+ if((diskData->RealGeometry.TracksPerCylinder *
+ diskData->RealGeometry.SectorsPerTrack) != 0) {
+
+ diskData->GeometrySource = DiskGeometryFromPort;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension));
+
+ if (diskData->RealGeometry.BytesPerSector == 0) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskDriverReinit: Port driver failed to "
+ "set BytesPerSector in the RealGeometry\n"));
+ diskData->RealGeometry.BytesPerSector =
+ FdoExtension->DiskGeometry.BytesPerSector;
+ if (diskData->RealGeometry.BytesPerSector == 0) {
+ NT_ASSERT(!"BytesPerSector is still zero!");
+ }
+
+ }
+ }
+ }
+ }
+
+ //
+ // If we came up with a "real" geometry for this drive then set it in the
+ // device extension.
+ //
+
+ if (diskData->GeometrySource != DiskGeometryUnknown) {
+
+ FdoExtension->DiskGeometry = diskData->RealGeometry;
+ }
+
+ return diskData->GeometrySource;
+}
+
+
+NTSTATUS
+DiskUpdateRemovableGeometry (
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine updates the geometry of the disk. It will query the port
+ driver to see if it can provide any geometry info. If not it will use
+ the current head & sector count.
+
+ Based on these values & the capacity of the drive as reported by
+ ClassReadDriveCapacity it will determine a new cylinder count for the
+ device.
+
+Arguments:
+
+ Fdo - Supplies the functional device object whos size needs to be updated.
+
+Return Value:
+
+ Returns the status of the opertion.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
+ PDISK_DATA diskData = commonExtension->DriverData;
+ PDISK_GEOMETRY geometry = &(diskData->RealGeometry);
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (FdoExtension->DeviceDescriptor) {
+ NT_ASSERT(FdoExtension->DeviceDescriptor->RemovableMedia);
+ }
+ NT_ASSERT(TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
+ FILE_REMOVABLE_MEDIA));
+
+ //
+ // Attempt to determine the disk geometry. First we'll check with the
+ // port driver to see what it suggests for a value.
+ //
+
+ status = DiskGetPortGeometry(FdoExtension, geometry);
+
+ if(NT_SUCCESS(status) &&
+ ((geometry->TracksPerCylinder * geometry->SectorsPerTrack) != 0)) {
+
+ FdoExtension->DiskGeometry = (*geometry);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskGetPortGeometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PDISK_GEOMETRY Geometry
+ )
+/*++
+
+Routine Description:
+
+ This routine will query the port driver for disk geometry. Some port
+ drivers (in particular IDEPORT) may be able to provide geometry for the
+ device.
+
+Arguments:
+
+ FdoExtension - the device object for the disk.
+
+ Geometry - a structure to save the geometry information into (if any is
+ available)
+
+Return Value:
+
+ STATUS_SUCCESS if geometry information can be provided or
+ error status indicating why it can't.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ KEVENT event;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver.
+ //
+
+ irp = IoAllocateIrp(commonExtension->LowerDeviceObject->StackSize, FALSE);
+
+ if(irp == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+
+ irpStack->Parameters.DeviceIoControl.IoControlCode =
+ IOCTL_DISK_GET_DRIVE_GEOMETRY;
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength =
+ sizeof(DISK_GEOMETRY);
+
+ irp->AssociatedIrp.SystemBuffer = Geometry;
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ IoSetCompletionRoutine(irp,
+ ClassSignalCompletion,
+ &event,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = irp->IoStatus.Status;
+ }
+
+ IoFreeIrp(irp);
+
+ return status;
+}
+
+
+BOOLEAN
+DiskIsNT4Geometry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+
+/*++
+
+Routine Description:
+
+ The default geometry that was used in partitioning disks under Windows NT 4.0 was
+
+ Sectors per Track = 0x20 = 32
+ Tracks per Cylinder = 0x40 = 64
+
+ This was changed in Windows 2000 to
+
+ Sectors per Track = 0x3F = 63
+ Tracks per Cylinder = 0xFF = 255
+
+ If neither the BIOS nor the port driver can report the correct geometry, we will
+ default to the new numbers on such disks. Now LVM uses the geometry when creating
+ logical volumes and dynamic disks. So reporting an incorrect geometry will cause
+ the entire extended partition / dynamic disk to be destroyed
+
+ In this routine, we will look at the Master Boot Record. In 90% of the cases, the
+ first entry corresponds to a partition that starts on the first track. If this is
+ so, we shall retrieve the logical block address associated with it and calculate
+ the correct geometry. Now, all partitions start on a cylinder boundary. So, for
+ the remaining 10% we will look at the ending CHS number to determine the geometry
+
+--*/
+
+{
+ PUSHORT readBuffer = NULL;
+ BOOLEAN bFoundNT4 = FALSE;
+
+ PAGED_CODE();
+
+ readBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, FdoExtension->DiskGeometry.BytesPerSector, DISK_TAG_UPDATE_GEOM);
+
+ if (readBuffer)
+ {
+ KEVENT event;
+ LARGE_INTEGER diskOffset;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ PIRP irp;
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ //
+ // Read the Master Boot Record at disk offset 0
+ //
+
+ diskOffset.QuadPart = 0;
+
+ irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, FdoExtension->DeviceObject, readBuffer, FdoExtension->DiskGeometry.BytesPerSector, &diskOffset, &event, &ioStatus);
+
+ if (irp)
+ {
+ PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp);
+ NTSTATUS status;
+
+ irpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
+ status = IoCallDriver(FdoExtension->DeviceObject, irp);
+
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Match the boot record signature
+ //
+
+ if (readBuffer[BOOT_SIGNATURE_OFFSET] == BOOT_RECORD_SIGNATURE)
+ {
+ PPARTITION_DESCRIPTOR partitionTableEntry = (PPARTITION_DESCRIPTOR)&readBuffer[PARTITION_TABLE_OFFSET];
+ ULONG uCount = 0;
+
+ //
+ // Walk the entries looking for a clue as to what the geometry might be
+ //
+
+ for (uCount = 0; uCount < NUM_PARTITION_TABLE_ENTRIES; uCount++)
+ {
+ //
+ // We are only concerned if there might be a logical volume or if this disk is part of a dynamic set
+ //
+
+ if (IsContainerPartition(partitionTableEntry->PartitionType) || partitionTableEntry->PartitionType == PARTITION_LDM)
+ {
+ //
+ // In 90% of the cases, the first entry corresponds to a partition that starts on the first track
+ //
+
+ if (partitionTableEntry->StartingTrack == 1 && GET_STARTING_SECTOR(partitionTableEntry) == 0x20)
+ {
+ bFoundNT4 = TRUE;
+ break;
+ }
+
+ //
+ // In almost every case, the ending CHS number is on a cylinder boundary
+ //
+
+ if (partitionTableEntry->EndingTrack == 0x3F && GET_ENDING_S_OF_CHS(partitionTableEntry) == 0x20)
+ {
+ bFoundNT4 = TRUE;
+ break;
+ }
+ }
+
+ partitionTableEntry++;
+ }
+ }
+ else
+ {
+ //
+ // The Master Boot Record is invalid
+ //
+ }
+ }
+ }
+
+ FREE_POOL(readBuffer);
+ }
+
+ return bFoundNT4;
+}
+
+
+NTSTATUS
+DiskReadDriveCapacity(
+ IN PDEVICE_OBJECT Fdo
+ )
+/*++
+
+Routine Description:
+
+ This routine is used by disk.sys as a wrapper for the classpnp API
+ ClassReadDriveCapacity. It will perform some additional operations to
+ attempt to determine drive geometry before it calls the classpnp version
+ of the routine.
+
+ For fixed disks this involves calling DiskUpdateGeometry which will check
+ various sources (the BIOS, the port driver) for geometry information.
+
+Arguments:
+
+ Fdo - a pointer to the device object to be checked.
+
+Return Value:
+
+ status of ClassReadDriveCapacity.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ NTSTATUS status;
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ DiskUpdateRemovableGeometry(fdoExtension);
+ } else {
+ DiskUpdateGeometry(fdoExtension);
+ }
+
+ status = ClassReadDriveCapacity(Fdo);
+
+ return status;
+}
+
+
+VOID
+DiskDriverReinitialization(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PVOID Nothing,
+ IN ULONG Count
+ )
+/*++
+
+Routine Description:
+
+ This routine will scan through the current list of disks and attempt to
+ match them to any remaining geometry information. This will only be done
+ on the first call to the routine.
+
+ Note: This routine assumes that the system will not be adding or removing
+ devices during this phase of the init process. This is very likely
+ a bad assumption but it greatly simplifies the code.
+
+Arguments:
+
+ DriverObject - a pointer to the object for the disk driver.
+
+ Nothing - unused
+
+ Count - an indication of how many times this routine has been called.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PDEVICE_OBJECT deviceObject;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PDISK_DATA diskData;
+
+ ULONG unmatchedDiskCount;
+ PDEVICE_OBJECT unmatchedDisk = NULL;
+
+ ULONG i;
+ PDISK_DETECT_INFO diskInfo = NULL;
+
+ UNREFERENCED_PARAMETER(Nothing);
+
+ if(Count != 1) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinitialization: ignoring call %d\n",
+ Count));
+ return;
+ }
+
+ //
+ // Check to see how many entries in the detect info list have been matched.
+ // If there's only one remaining we'll see if we can find a disk to go with
+ // it.
+ //
+
+ if(DetectInfoCount == 0) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinitialization: no detect info saved\n"));
+ return;
+ }
+
+ if((DetectInfoCount - DetectInfoUsedCount) != 1) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinitialization: %d of %d geometry entries "
+ "used - will not attempt match\n", DetectInfoUsedCount, DetectInfoCount));
+ return;
+ }
+
+ //
+ // Scan through the list of disks and see if any of them are missing
+ // geometry information. If there is only one such disk we'll try to
+ // match it to the unmatched geometry.
+ //
+
+
+ //
+ // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep
+ // removals from happening while doing this.
+ //
+
+ for(deviceObject = DriverObject->DeviceObject, unmatchedDiskCount = 0;
+ deviceObject != NULL;
+#pragma prefast(suppress:28175, "Need to access the opaque field to scan through the list of disks")
+ deviceObject = deviceObject->NextDevice) {
+
+ fdoExtension = deviceObject->DeviceExtension;
+
+ if (!fdoExtension->CommonExtension.IsFdo) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: %#p is not an FDO\n",
+ deviceObject));
+ continue;
+ }
+
+ //
+ // If the geometry for this one is already known then skip it.
+ //
+
+ diskData = fdoExtension->CommonExtension.DriverData;
+ if(diskData->GeometrySource != DiskGeometryUnknown) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: FDO %#p has a geometry\n",
+ deviceObject));
+ continue;
+ }
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinit: FDO %#p has no geometry\n",
+ deviceObject));
+
+ //
+ // Mark this one as using the default. It's past the time when disk
+ // might blunder across the geometry info. If we set the geometry
+ // from the bios we'll reset this field down below.
+ //
+
+ diskData->GeometrySource = DiskGeometryFromDefault;
+
+ //
+ // As long as we've only got one unmatched disk we're fine.
+ //
+
+ unmatchedDiskCount++;
+ if(unmatchedDiskCount > 1) {
+ NT_ASSERT(unmatchedDisk != NULL);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinit: FDO %#p also has no geometry\n",
+ unmatchedDisk));
+ unmatchedDisk = NULL;
+ break;
+ }
+
+ unmatchedDisk = deviceObject;
+ }
+
+ //
+ // If there's more or less than one ungeometried disk then we can't do
+ // anything about the geometry.
+ //
+
+ if(unmatchedDiskCount != 1) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskDriverReinit: Unable to match geometry\n"));
+ return;
+
+ }
+
+ fdoExtension = unmatchedDisk->DeviceExtension;
+ diskData = fdoExtension->CommonExtension.DriverData;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: Found possible match\n"));
+
+ //
+ // Find the geometry which wasn't assigned.
+ //
+
+ for(i = 0; i < DetectInfoCount; i++) {
+ if(DetectInfoList[i].Device == NULL) {
+ diskInfo = &(DetectInfoList[i]);
+ break;
+ }
+ }
+
+ if (diskInfo != NULL)
+ {
+ //
+ // Save the geometry information away in the disk data block and
+ // set the bit indicating that we found a valid one.
+ //
+
+ ULONG cylinders;
+ ULONG sectorsPerTrack;
+ ULONG tracksPerCylinder;
+
+ ULONG length;
+
+ //
+ // Point to the array of drive parameters.
+ //
+
+ cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
+ sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
+ tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
+
+ //
+ // Since the BIOS may not report the full drive, recalculate the drive
+ // size based on the volume size and the BIOS values for tracks per
+ // cylinder and sectors per track..
+ //
+
+ length = tracksPerCylinder * sectorsPerTrack;
+
+ if (length == 0) {
+
+ //
+ // The BIOS information is bogus.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: H (%d) or S(%d) is zero\n",
+ tracksPerCylinder, sectorsPerTrack));
+ return;
+ }
+
+ //
+ // since we are copying the structure RealGeometry here, we should
+ // really initialize all the fields, especially since a zero'd
+ // BytesPerSector field would cause a trap in xHalReadPartitionTable()
+ //
+
+ diskData->RealGeometry = fdoExtension->DiskGeometry;
+
+ //
+ // Save the geometry information away in the disk data block and
+ // set the bit indicating that we found a valid one.
+ //
+
+ diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
+ diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
+ diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: BIOS spt %#x, #heads %#x, "
+ "#cylinders %#x\n",
+ sectorsPerTrack, tracksPerCylinder, cylinders));
+
+ diskData->GeometrySource = DiskGeometryGuessedFromBios;
+ diskInfo->Device = unmatchedDisk;
+
+ //
+ // Now copy the geometry over to the fdo extension and call
+ // classpnp to redetermine the disk size and cylinder count.
+ //
+
+ fdoExtension->DiskGeometry = diskData->RealGeometry;
+
+ (VOID)ClassReadDriveCapacity(unmatchedDisk);
+
+ if (diskData->RealGeometry.BytesPerSector == 0) {
+
+ //
+ // if the BytesPerSector field is set to zero for a disk
+ // listed in the bios, then the system will bugcheck in
+ // xHalReadPartitionTable(). assert here since it is
+ // easier to determine what is happening this way.
+ //
+
+ NT_ASSERT(!"RealGeometry not set to non-zero bps\n");
+ }
+ }
+
+ return;
+}
+
+
+NTSTATUS
+DiskGetDetectInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PDISK_DETECTION_INFO DetectInfo
+ )
+/*++
+
+Routine Description:
+
+ Get the Int13 information from the BIOS DetectInfoList.
+
+Arguments:
+
+ FdoExtension - Supplies a pointer to the FDO extension that we want to
+ obtain the detect information for.
+
+ DetectInfo - A buffer where the detect information will be copied to.
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+{
+ ULONG i;
+ BOOLEAN found = FALSE;
+ PDISK_DETECT_INFO diskInfo = NULL;
+ PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
+
+ PAGED_CODE ();
+
+ //
+ // Fail for non-fixed drives.
+ //
+
+ if (TEST_FLAG (FdoExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // There is no GPT detection info, so fail this.
+ //
+
+ if (diskData->PartitionStyle == PARTITION_STYLE_GPT) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ for(i = 0; i < DetectInfoCount; i++) {
+
+
+ NT_ASSERT(DetectInfoList != NULL);
+
+ diskInfo = &(DetectInfoList[i]);
+
+ if((diskData->Mbr.Signature != 0) &&
+ (diskData->Mbr.Signature == diskInfo->Signature)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskGetDetectInfo: found match for signature "
+ "%#08lx\n",
+ diskData->Mbr.Signature));
+ found = TRUE;
+ break;
+ } else if((diskData->Mbr.Signature == 0) &&
+ (diskData->Mbr.MbrCheckSum != 0) &&
+ (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskGetDetectInfo: found match for xsum %#08lx\n",
+ diskData->Mbr.MbrCheckSum));
+ found = TRUE;
+ break;
+ }
+ }
+
+ if ( found ) {
+ DetectInfo->DetectionType = DetectInt13;
+ DetectInfo->Int13.DriveSelect = diskInfo->DriveParameters.DriveSelect;
+ DetectInfo->Int13.MaxCylinders = diskInfo->DriveParameters.MaxCylinders;
+ DetectInfo->Int13.SectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
+ DetectInfo->Int13.MaxHeads = diskInfo->DriveParameters.MaxHeads;
+ DetectInfo->Int13.NumberDrives = diskInfo->DriveParameters.NumberDrives;
+ RtlZeroMemory (&DetectInfo->ExInt13, sizeof (DetectInfo->ExInt13));
+ }
+
+ return (found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
+}
+
+
+NTSTATUS
+DiskReadSignature(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ Read the disks signature from the drive. The signature can be either
+ a MBR signature or a GPT/EFI signature.
+
+ The low-level signature reading is done by IoReadDiskSignature().
+
+Arguments:
+
+ Fdo - Pointer to the FDO of a disk to read the signature for.
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+
+
+{
+ NTSTATUS Status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
+ DISK_SIGNATURE Signature = { 0 };
+
+ PAGED_CODE();
+
+ Status = IoReadDiskSignature (Fdo,
+ fdoExtension->DiskGeometry.BytesPerSector,
+ &Signature);
+
+ if (!NT_SUCCESS (Status)) {
+ return Status;
+ }
+
+ if (Signature.PartitionStyle == PARTITION_STYLE_GPT) {
+ diskData->PartitionStyle = PARTITION_STYLE_GPT;
+ diskData->Efi.DiskId = Signature.Gpt.DiskId;
+ } else if (Signature.PartitionStyle == PARTITION_STYLE_MBR) {
+ diskData->PartitionStyle = PARTITION_STYLE_MBR;
+ diskData->Mbr.Signature = Signature.Mbr.Signature;
+ diskData->Mbr.MbrCheckSum = Signature.Mbr.CheckSum;
+ } else {
+ NT_ASSERT (FALSE);
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ return Status;
+}
+
+#endif // defined(_X86_) || defined(_AMD64_)
+
diff --git a/storage/class/disk/src/pnp.c b/storage/class/disk/src/pnp.c
new file mode 100644
index 00000000..bb3a33c5
--- /dev/null
+++ b/storage/class/disk/src/pnp.c
@@ -0,0 +1,1098 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ pnp.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "disk.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "pnp.tmh"
+#endif
+
+extern PULONG InitSafeBootMode;
+ULONG diskDeviceSequenceNumber = 0;
+extern BOOLEAN DiskIsPastReinit;
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DiskAddDevice)
+#pragma alloc_text(PAGE, DiskInitFdo)
+#pragma alloc_text(PAGE, DiskStartFdo)
+#pragma alloc_text(PAGE, DiskGenerateDeviceName)
+#pragma alloc_text(PAGE, DiskCreateSymbolicLinks)
+#pragma alloc_text(PAGE, DiskDeleteSymbolicLinks)
+#pragma alloc_text(PAGE, DiskRemoveDevice)
+#endif
+
+
+NTSTATUS
+DiskAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT PhysicalDeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets a port drivers capabilities, obtains the
+ inquiry data, searches the SCSI bus for the port driver and creates
+ the device objects for the disks found.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ Pdo - Device object use to send requests to port driver.
+
+Return Value:
+
+ True is returned if one disk was found and successfully created.
+
+--*/
+
+{
+ ULONG rootPartitionMountable = FALSE;
+
+ PCONFIGURATION_INFORMATION configurationInformation;
+ ULONG diskCount;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // See if we should be allowing file systems to mount on partition zero.
+ //
+
+ TRY {
+ HANDLE deviceKey = NULL;
+
+ UNICODE_STRING diskKeyName;
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ HANDLE diskKey;
+
+ RTL_QUERY_REGISTRY_TABLE queryTable[2] = { 0 };
+
+ status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ,
+ &deviceKey);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening device key "
+ "for pdo %p\n",
+ status, PhysicalDeviceObject));
+ LEAVE;
+ }
+
+ RtlInitUnicodeString(&diskKeyName, L"Disk");
+ InitializeObjectAttributes(&objectAttributes,
+ &diskKeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceKey,
+ NULL);
+
+ status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
+ ZwClose(deviceKey);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening disk key "
+ "for pdo %p device key %p\n",
+ status, PhysicalDeviceObject, deviceKey));
+ LEAVE;
+ }
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].Name = L"RootPartitionMountable";
+ queryTable[0].EntryContext = &(rootPartitionMountable);
+
+#pragma prefast(suppress:6309, "We don't have QueryRoutine so Context doesn't make any sense")
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ diskKey,
+ queryTable,
+ NULL,
+ NULL);
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx reading value from "
+ "disk key %p for pdo %p\n",
+ status, diskKey, PhysicalDeviceObject));
+ }
+
+ ZwClose(diskKey);
+
+ } FINALLY {
+
+ //
+ // Do nothing.
+ //
+
+ if(!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskAddDevice: Will %sallow file system to mount on "
+ "partition zero of disk %p\n",
+ (rootPartitionMountable ? "" : "not "),
+ PhysicalDeviceObject));
+ }
+ }
+
+ //
+ // Create device objects for disk
+ //
+
+ diskCount = 0;
+
+ status = DiskCreateFdo(
+ DriverObject,
+ PhysicalDeviceObject,
+ &diskCount,
+ (BOOLEAN) !rootPartitionMountable
+ );
+
+ //
+ // Get the number of disks already initialized.
+ //
+
+ configurationInformation = IoGetConfigurationInformation();
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Increment system disk device count.
+ //
+
+ configurationInformation->DiskCount++;
+
+ }
+
+ return status;
+
+} // end DiskAddDevice()
+
+
+NTSTATUS
+DiskInitFdo(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to do one-time initialization of new device objects
+
+
+Arguments:
+
+ Fdo - a pointer to the functional device object for this device
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData;
+
+ ULONG srbFlags = 0;
+ ULONG timeOut = 0;
+ ULONG bytesPerSector;
+
+ PULONG dmSkew;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // Build the lookaside list for srb's for the physical disk. Should only
+ // need a couple. If this fails then we don't have an emergency SRB so
+ // fail the call to initialize.
+ //
+
+ ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension,
+ PARTITION0_LIST_SIZE);
+
+ if (fdoExtension->DeviceDescriptor->RemovableMedia)
+ {
+ SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA);
+ }
+
+ //
+ // Initialize the srb flags.
+ //
+
+ //
+ // Because all requests share a common sense buffer, it is possible
+ // for the buffer to be overwritten if the port driver completes
+ // multiple failed requests that require a request sense before the
+ // class driver's completion routine can consume the data in the buffer.
+ // To prevent this, we allow the port driver to allocate a unique sense
+ // buffer each time it needs one. We are responsible for freeing this
+ // buffer. This also allows the adapter to be configured to support
+ // additional sense data beyond the minimum 18 bytes.
+ //
+
+ SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE);
+
+ if (fdoExtension->DeviceDescriptor->CommandQueueing &&
+ fdoExtension->AdapterDescriptor->CommandQueueing) {
+
+ SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ }
+
+ //
+ // Look for controllers that require special flags.
+ //
+
+ ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks);
+
+ //
+ // Clear buffer for drive geometry.
+ //
+
+ RtlZeroMemory(&(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ //
+ // Allocate request sense buffer.
+ //
+
+ fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ SENSE_BUFFER_SIZE_EX,
+ DISK_TAG_START);
+
+ if (fdoExtension->SenseData == NULL) {
+
+ //
+ // The buffer can not be allocated.
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskInitFdo: Can not allocate request sense buffer\n"));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ return status;
+ }
+
+ //
+ // Set the buffer size of SenseData
+ //
+
+ fdoExtension->SenseDataLength = SENSE_BUFFER_SIZE_EX;
+
+ //
+ // Physical device object will describe the entire
+ // device, starting at byte offset 0.
+ //
+
+ fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0);
+
+ //
+ // Set timeout value in seconds.
+ //
+ if ( (fdoExtension->MiniportDescriptor != NULL) &&
+ (fdoExtension->MiniportDescriptor->IoTimeoutValue > 0) ) {
+ //
+ // use the value set by Storport miniport driver
+ //
+ fdoExtension->TimeOutValue = fdoExtension->MiniportDescriptor->IoTimeoutValue;
+ } else {
+ //
+ // get timeout value from registry
+ //
+ timeOut = ClassQueryTimeOutRegistryValue(Fdo);
+
+ if (timeOut) {
+ fdoExtension->TimeOutValue = timeOut;
+ } else {
+ fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
+ }
+ }
+ //
+ // If this is a removable drive, build an entry in devicemap\scsi
+ // indicating it's physicaldriveN name, set up the appropriate
+ // update partitions routine and set the flags correctly.
+ // note: only do this after the timeout value is set, above.
+ //
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ ClassUpdateInformationInRegistry( Fdo,
+ "PhysicalDrive",
+ fdoExtension->DeviceNumber,
+ NULL,
+ 0);
+ //
+ // Enable media change notification for removable disks
+ //
+ ClassInitializeMediaChangeDetection(fdoExtension,
+ (PUCHAR)"Disk");
+
+ } else {
+
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
+ SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ }
+
+ //
+ // The commands we send during the init could cause the flags to change
+ // in case of any error. Save the SRB flags locally and restore it at
+ // the end of this function, so that the class driver can get it.
+ //
+
+ srbFlags = fdoExtension->SrbFlags;
+
+
+ //
+ // Read the drive capacity. Don't use the disk version of the routine here
+ // since we don't know the disk signature yet - the disk version will
+ // attempt to determine the BIOS reported geometry.
+ //
+
+ (VOID)ClassReadDriveCapacity(Fdo);
+
+ //
+ // Set up sector size fields.
+ //
+ // Stack variables will be used to update
+ // the partition device extensions.
+ //
+ // The device extension field SectorShift is
+ // used to calculate sectors in I/O transfers.
+ //
+ // The DiskGeometry structure is used to service
+ // IOCTls used by the format utility.
+ //
+
+ bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+
+ //
+ // Make sure sector size is not zero.
+ //
+
+ if (bytesPerSector == 0) {
+
+ //
+ // Default sector size for disk is 512.
+ //
+
+ bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512;
+ fdoExtension->SectorShift = 9;
+ }
+
+ //
+ // Determine is DM Driver is loaded on an IDE drive that is
+ // under control of Atapi - this could be either a crashdump or
+ // an Atapi device is sharing the controller with an IDE disk.
+ //
+
+ HalExamineMBR(fdoExtension->CommonExtension.DeviceObject,
+ fdoExtension->DiskGeometry.BytesPerSector,
+ (ULONG)0x54,
+ &dmSkew);
+
+ if (dmSkew) {
+
+ //
+ // Update the device extension, so that the call to IoReadPartitionTable
+ // will get the correct information. Any I/O to this disk will have
+ // to be skewed by *dmSkew sectors aka DMByteSkew.
+ //
+
+ fdoExtension->DMSkew = *dmSkew;
+ fdoExtension->DMActive = TRUE;
+ fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector;
+
+ FREE_POOL(dmSkew);
+ }
+
+#if defined(_X86_) || defined(_AMD64_)
+
+ //
+ // Try to read the signature off the disk and determine the correct drive
+ // geometry based on that. This requires rereading the disk size to get
+ // the cylinder count updated correctly.
+ //
+
+ if(!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ DiskReadSignature(Fdo);
+ DiskReadDriveCapacity(Fdo);
+
+ if (diskData->GeometrySource == DiskGeometryUnknown)
+ {
+ //
+ // Neither the BIOS nor the port driver could provide us with a reliable
+ // geometry. Before we use the default, look to see if it was partitioned
+ // under Windows NT4 [or earlier] and apply the one that was used back then
+ //
+
+ if (DiskIsNT4Geometry(fdoExtension))
+ {
+ diskData->RealGeometry = fdoExtension->DiskGeometry;
+ diskData->RealGeometry.SectorsPerTrack = 0x20;
+ diskData->RealGeometry.TracksPerCylinder = 0x40;
+ fdoExtension->DiskGeometry = diskData->RealGeometry;
+
+ diskData->GeometrySource = DiskGeometryFromNT4;
+ }
+ }
+ }
+
+#endif
+
+ DiskCreateSymbolicLinks(Fdo);
+
+ //
+ // Get the SCSI address if it's available for use with SMART ioctls.
+ // SMART ioctls are used for failure prediction, so we need to get
+ // the SCSI address before initializing failure prediction.
+ //
+
+ {
+ PIRP irp;
+ KEVENT event;
+ IO_STATUS_BLOCK statusBlock = { 0 };
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
+ fdoExtension->CommonExtension.LowerDeviceObject,
+ NULL,
+ 0L,
+ &(diskData->ScsiAddress),
+ sizeof(SCSI_ADDRESS),
+ FALSE,
+ &event,
+ &statusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+
+ if(irp != NULL) {
+
+ status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ if(status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = statusBlock.Status;
+ }
+ }
+ }
+
+ //
+ // Determine the type of disk and enable failure prediction in the hardware
+ // and enable failure prediction polling.
+ //
+
+ if (*InitSafeBootMode == 0)
+ {
+ DiskDetectFailurePrediction(fdoExtension,
+ &diskData->FailurePredictionCapability,
+ NT_SUCCESS(status));
+
+ if (diskData->FailurePredictionCapability != FailurePredictionNone)
+ {
+ //
+ // Cool, we've got some sort of failure prediction, enable it
+ // at the hardware and then enable polling for it
+ //
+
+ //
+ // By default we allow performance to be degradeded if failure
+ // prediction is enabled.
+ //
+
+ diskData->AllowFPPerfHit = TRUE;
+
+ //
+ // Enable polling only after Atapi and SBP2 add support for the new
+ // SRB flag that indicates that the request should not reset the
+ // drive spin down idle timer.
+ //
+
+ status = DiskEnableDisableFailurePredictPolling(fdoExtension,
+ TRUE,
+ DISK_DEFAULT_FAILURE_POLLING_PERIOD);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskInitFdo: Failure Prediction Poll enabled as "
+ "%d for device %p, Status = %lx\n",
+ diskData->FailurePredictionCapability,
+ Fdo,
+ status));
+ }
+ } else {
+
+ //
+ // In safe boot mode we do not enable failure prediction, as perhaps
+ // it is the reason why normal boot does not work
+ //
+
+ diskData->FailurePredictionCapability = FailurePredictionNone;
+
+ }
+
+ //
+ // Initialize the verify mutex
+ //
+
+ KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY);
+
+ //
+ // Initialize the flush group context
+ //
+
+ RtlZeroMemory(&diskData->FlushContext, sizeof(DISK_GROUP_CONTEXT));
+
+ InitializeListHead(&diskData->FlushContext.CurrList);
+ InitializeListHead(&diskData->FlushContext.NextList);
+
+ KeInitializeSpinLock(&diskData->FlushContext.Spinlock);
+ KeInitializeEvent(&diskData->FlushContext.Event, SynchronizationEvent, FALSE);
+
+
+ //
+ // Restore the saved value
+ //
+ fdoExtension->SrbFlags = srbFlags;
+
+ return STATUS_SUCCESS;
+
+} // end DiskInitFdo()
+
+NTSTATUS
+DiskStopDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ )
+
+{
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Type);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DiskGenerateDeviceName(
+ IN ULONG DeviceNumber,
+ OUT PCCHAR *RawName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will allocate a unicode string buffer and then fill it in
+ with a generated name for the specified device object.
+
+ It is the responsibility of the user to allocate a UNICODE_STRING structure
+ to pass in and to free UnicodeName->Buffer when done with it.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+
+ UnicodeName - a unicode string to put the name buffer into
+
+Return Value:
+
+ status
+
+--*/
+
+#define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d"
+
+{
+ CHAR rawName[64] = { 0 };
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ status = RtlStringCchPrintfA(rawName, sizeof(rawName) - 1, FDO_NAME_FORMAT, DeviceNumber,
+ diskDeviceSequenceNumber++);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Format FDO name failed with error: 0x%X\n", status));
+ return status;
+ }
+
+ *RawName = ExAllocatePoolWithTag(PagedPool,
+ strlen(rawName) + 1,
+ DISK_TAG_NAME);
+
+ if(*RawName == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = RtlStringCchCopyA(*RawName, strlen(rawName) + 1, rawName);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Device name copy failed with error: 0x%X\n", status));
+ FREE_POOL(*RawName);
+ return status;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskGenerateDeviceName: generated \"%s\"\n", rawName));
+
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+DiskCreateSymbolicLinks(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will generate a symbolic link for the specified device object
+ using the well known form \\Device\HarddiskX\PartitionY, where X and Y is
+ always 0 which represents the entire disk object.
+
+ This routine will not try to delete any previous symbolic link for the
+ same generated name - the caller must make sure the symbolic link has
+ been broken before calling this routine.
+
+Arguments:
+
+ DeviceObject - the device object to make a well known name for
+
+Return Value:
+
+ STATUS
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = commonExtension->DriverData;
+
+ WCHAR wideSourceName[64] = { 0 };
+ UNICODE_STRING unicodeSourceName;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Build the destination for the link first using the device name
+ // stored in the device object
+ //
+
+ NT_ASSERT(commonExtension->DeviceName.Buffer);
+
+ if(!diskData->LinkStatus.WellKnownNameCreated) {
+ //
+ // Put together the source name using the partition and device number
+ // in the device extension and disk data segment
+ //
+
+ status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1,
+ L"\\Device\\Harddisk%d\\Partition0",
+ commonExtension->PartitionZeroExtension->DeviceNumber);
+
+ if (NT_SUCCESS(status)) {
+
+ RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
+ &unicodeSourceName,
+ &commonExtension->DeviceName));
+
+ status = IoCreateSymbolicLink(&unicodeSourceName,
+ &commonExtension->DeviceName);
+
+ if(NT_SUCCESS(status)){
+ diskData->LinkStatus.WellKnownNameCreated = TRUE;
+ }
+ }
+ }
+
+ if (!diskData->LinkStatus.PhysicalDriveLinkCreated) {
+
+ //
+ // Create a physical drive N link using the device number we saved
+ // away during AddDevice.
+ //
+
+ status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1,
+ L"\\DosDevices\\PhysicalDrive%d",
+ commonExtension->PartitionZeroExtension->DeviceNumber);
+ if (NT_SUCCESS(status)) {
+
+ RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
+ &unicodeSourceName,
+ &(commonExtension->DeviceName)));
+
+ status = IoCreateSymbolicLink(&unicodeSourceName,
+ &(commonExtension->DeviceName));
+
+ if(NT_SUCCESS(status)) {
+ diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE;
+ }
+ }
+ }
+
+
+ return;
+}
+
+
+VOID
+DiskDeleteSymbolicLinks(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will delete the well known name (symlink) for the specified
+ device. It generates the link name using information stored in the
+ device extension
+
+Arguments:
+
+ DeviceObject - the device object we are unlinking
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = commonExtension->DriverData;
+
+ WCHAR wideLinkName[64] = { 0 };
+ UNICODE_STRING unicodeLinkName;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if(diskData->LinkStatus.WellKnownNameCreated) {
+
+ status = RtlStringCchPrintfW(wideLinkName, sizeof(wideLinkName) / sizeof(wideLinkName[0]) - 1,
+ L"\\Device\\Harddisk%d\\Partition0",
+ commonExtension->PartitionZeroExtension->DeviceNumber);
+ if (NT_SUCCESS(status)) {
+ RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
+ IoDeleteSymbolicLink(&unicodeLinkName);
+ }
+ diskData->LinkStatus.WellKnownNameCreated = FALSE;
+ }
+
+ if(diskData->LinkStatus.PhysicalDriveLinkCreated) {
+
+ status = RtlStringCchPrintfW(wideLinkName, sizeof(wideLinkName) / sizeof(wideLinkName[0]) - 1,
+ L"\\DosDevices\\PhysicalDrive%d",
+ commonExtension->PartitionZeroExtension->DeviceNumber);
+ if (NT_SUCCESS(status)) {
+ RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
+ IoDeleteSymbolicLink(&unicodeLinkName);
+ }
+ diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE;
+ }
+
+
+ return;
+}
+
+
+NTSTATUS
+DiskRemoveDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will release any resources the device may have allocated for
+ this device object and return.
+
+Arguments:
+
+ DeviceObject - the device object being removed
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PAGED_CODE();
+
+ //
+ // Handle query and cancel
+ //
+
+ if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
+ (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Delete our object directory.
+ //
+
+ if(fdoExtension->DeviceDirectory != NULL) {
+ ZwMakeTemporaryObject(fdoExtension->DeviceDirectory);
+ ZwClose(fdoExtension->DeviceDirectory);
+ fdoExtension->DeviceDirectory = NULL;
+ }
+
+ if(Type == IRP_MN_REMOVE_DEVICE) {
+
+ FREE_POOL(fdoExtension->SenseData);
+
+ IoGetConfigurationInformation()->DiskCount--;
+
+ }
+
+ DiskDeleteSymbolicLinks(DeviceObject);
+
+ if (Type == IRP_MN_REMOVE_DEVICE)
+ {
+ ClassDeleteSrbLookasideList(commonExtension);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DiskStartFdo(
+ IN PDEVICE_OBJECT Fdo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will query the underlying device for any information necessary
+ to complete initialization of the device. This will include physical
+ disk geometry, mode sense information and such.
+
+ This routine does not perform partition enumeration - that is left to the
+ re-enumeration routine
+
+ If this routine fails it will return an error value. It does not clean up
+ any resources - that is left for the Stop/Remove routine.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object for this device
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension);
+ PDISK_DATA diskData = commonExtension->DriverData;
+ STORAGE_HOTPLUG_INFO hotplugInfo = { 0 };
+ DISK_CACHE_INFORMATION cacheInfo = { 0 };
+ ULONG isPowerProtected = 0;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Get the hotplug information, so we can turn off write cache if needed
+ //
+ // NOTE: Capabilities info is not good enough to determine hotplugedness
+ // as we cannot determine device relations information and other
+ // dependencies. Get the hotplug info instead
+ //
+
+ {
+ PIRP irp;
+ KEVENT event;
+ IO_STATUS_BLOCK statusBlock = { 0 };
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO,
+ Fdo,
+ NULL,
+ 0L,
+ &hotplugInfo,
+ sizeof(STORAGE_HOTPLUG_INFO),
+ FALSE,
+ &event,
+ &statusBlock);
+
+ if (irp != NULL) {
+
+ // send to self -- classpnp handles this
+ status = IoCallDriver(Fdo, irp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ status = statusBlock.Status;
+ }
+ NT_ASSERT(NT_SUCCESS(status));
+ }
+ }
+
+ //
+ // Clear the DEV_WRITE_CACHE flag now and set
+ // it below only if we read that from the disk
+ //
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ diskData->WriteCacheOverride = DiskWriteCacheDefault;
+
+ //
+ // Look into the registry to see if the user
+ // has chosen to override the default setting
+ //
+ ClassGetDeviceParameter(fdoExtension,
+ DiskDeviceParameterSubkey,
+ DiskDeviceUserWriteCacheSetting,
+ (PULONG)&diskData->WriteCacheOverride);
+
+ if (diskData->WriteCacheOverride == DiskWriteCacheDefault)
+ {
+ //
+ // The user has not overridden the default settings
+ //
+ if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE))
+ {
+ //
+ // This flag indicates that we have faulty firmware and this
+ // may cause the filesystem to refuse to mount on this media
+ //
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to a firmware issue\n", Fdo));
+
+ diskData->WriteCacheOverride = DiskWriteCacheDisable;
+ }
+ else if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride)
+ {
+ //
+ // This flag indicates that the device is hotpluggable making it unsafe to enable caching
+ //
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to hotpluggable device\n", Fdo));
+
+ diskData->WriteCacheOverride = DiskWriteCacheDisable;
+ }
+ else if (hotplugInfo.MediaHotplug)
+ {
+ //
+ // This flag indicates that the media in the device cannot be reliably locked
+ //
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to unlockable media\n", Fdo));
+
+ diskData->WriteCacheOverride = DiskWriteCacheDisable;
+ }
+ else
+ {
+ //
+ // Even though the device does not seem to have any obvious problems
+ // we leave it to the user to modify the previous write cache setting
+ //
+ }
+ }
+
+ //
+ // Query the disk to see if write cache is enabled
+ // and set the DEV_WRITE_CACHE flag appropriately
+ //
+
+ status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
+
+ if (NT_SUCCESS(status))
+ {
+ if (cacheInfo.WriteCacheEnabled == TRUE)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ if (diskData->WriteCacheOverride == DiskWriteCacheDisable)
+ {
+ //
+ // Write cache is currently enabled on this
+ // device, but we would like to turn it off
+ //
+ cacheInfo.WriteCacheEnabled = FALSE;
+
+ DiskSetCacheInformation(fdoExtension, &cacheInfo);
+ }
+ }
+ else
+ {
+ if (diskData->WriteCacheOverride == DiskWriteCacheEnable)
+ {
+ //
+ // Write cache is currently disabled on this
+ // device, but we would like to turn it on
+ //
+ cacheInfo.WriteCacheEnabled = TRUE;
+
+ DiskSetCacheInformation(fdoExtension, &cacheInfo);
+ }
+ }
+ }
+
+ //
+ // Query the registry to see if this disk is power-protected or not
+ //
+
+ CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+
+ ClassGetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, &isPowerProtected);
+
+ if (isPowerProtected == 1)
+ {
+ SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+ }
+
+ ADJUST_FUA_FLAG(fdoExtension);
+
+ return STATUS_SUCCESS;
+
+} // end DiskStartFdo()
+