diff options
| author | Dave Wilson <[email protected]> | 2015-04-29 09:48:49 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-04-29 10:47:29 -0700 |
| commit | d40232638faaab19de557e70a3ee938da7a35295 (patch) | |
| tree | 14e61c973745949af186b7940e3a53d25322f5de /storage/class/disk/src | |
| parent | 02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff) | |
samples update for //build
Diffstat (limited to 'storage/class/disk/src')
| -rw-r--r-- | storage/class/disk/src/data.c | 85 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.c | 6166 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.h | 1257 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.htm | 42 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.rc | 23 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.vcxproj | 195 | ||||
| -rw-r--r-- | storage/class/disk/src/disk.vcxproj.Filters | 43 | ||||
| -rw-r--r-- | storage/class/disk/src/diskdev.inf | 113 | ||||
| -rw-r--r-- | storage/class/disk/src/diskwmi.c | 3501 | ||||
| -rw-r--r-- | storage/class/disk/src/enum.c | 1313 | ||||
| -rw-r--r-- | storage/class/disk/src/geometry.c | 1672 | ||||
| -rw-r--r-- | storage/class/disk/src/pnp.c | 1098 |
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, §orOffset); + REVERSE_BYTES_SHORT(&Cdb->CDB16.TransferLength[2], &numSectors); + } else { + + // + // Move little endian values into CDB in big endian format + // + + Cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3; + Cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2; + Cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1; + Cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->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		 Description +</U> +Data.c	 List of workarounds for some drives (no code) +Disk.c	 Main code base +Disk.h	 Private header files +Disk.rc	 Resource file +Diskwmi.c	 S.M.A.R.T. WMI implementation +Drivesup.c	 No longer used +Drivesup.h	 No longer used +Enum.c	 Enumeration routines for disk drives +Makefile	 Makefile +Pnp.c	 Start/add/pnp code +Sources	 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>© 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() + |
