From f144b8c10d4971f91fc3bcdb964d6a0e57fbbd6c Mon Sep 17 00:00:00 2001 From: Victor Perevertkin Date: Sun, 30 Aug 2020 03:43:14 +0300 Subject: [PATCH] [DISK_NEW] Import Microsoft Disk class driver from GitHub The source code is licensed under MS-PL license, taken from Windows Driver Samples repository (https://github.com/microsoft/Windows-driver-samples/tree/master/storage/class/disk/) Synched with commit 3428c5feaac7c1a5e2a09db0ed93392f7568f9e6 The driver is written for Windows 8+, so we compile it with ntoskrnl_vista statically linked and with NTDDI_WIN8 defined CORE-17129 --- drivers/storage/class/disk_new/CMakeLists.txt | 32 + drivers/storage/class/disk_new/data.c | 88 + drivers/storage/class/disk_new/disk.c | 6195 +++++++++++++++++ drivers/storage/class/disk_new/disk.h | 1288 ++++ drivers/storage/class/disk_new/disk.inf | 129 + drivers/storage/class/disk_new/disk.rc | 23 + drivers/storage/class/disk_new/disk_reg.inf | 7 + drivers/storage/class/disk_new/diskwmi.c | 3510 ++++++++++ drivers/storage/class/disk_new/geometry.c | 1676 +++++ drivers/storage/class/disk_new/license.txt | 23 + drivers/storage/class/disk_new/pnp.c | 1108 +++ 11 files changed, 14079 insertions(+) create mode 100644 drivers/storage/class/disk_new/CMakeLists.txt create mode 100644 drivers/storage/class/disk_new/data.c create mode 100644 drivers/storage/class/disk_new/disk.c create mode 100644 drivers/storage/class/disk_new/disk.h create mode 100644 drivers/storage/class/disk_new/disk.inf create mode 100644 drivers/storage/class/disk_new/disk.rc create mode 100644 drivers/storage/class/disk_new/disk_reg.inf create mode 100644 drivers/storage/class/disk_new/diskwmi.c create mode 100644 drivers/storage/class/disk_new/geometry.c create mode 100644 drivers/storage/class/disk_new/license.txt create mode 100644 drivers/storage/class/disk_new/pnp.c diff --git a/drivers/storage/class/disk_new/CMakeLists.txt b/drivers/storage/class/disk_new/CMakeLists.txt new file mode 100644 index 00000000000..8c11bc30a72 --- /dev/null +++ b/drivers/storage/class/disk_new/CMakeLists.txt @@ -0,0 +1,32 @@ + +remove_definitions(-D_WIN32_WINNT=0x502) + +list(APPEND SOURCE + data.c + disk.c + diskwmi.c + geometry.c + pnp.c + disk.h) + +add_library(disk MODULE ${SOURCE} disk.rc) + +target_compile_definitions(disk PUBLIC + DEBUG_USE_KDPRINT + _WIN32_WINNT=0x602 + NTDDI_VERSION=0x06020000) # NTDDI_WIN8 + +if(USE_CLANG_CL OR (NOT MSVC)) + target_compile_options(disk PRIVATE -Wno-format -Wno-pointer-sign) +endif() + +if(GCC) + target_compile_options(disk PRIVATE -Wno-unused-but-set-variable -Wno-pointer-to-int-cast -Wno-switch) +endif() + +set_module_type(disk kernelmodedriver) +target_link_libraries(disk ntoskrnl_vista libcntpr wdmguid) +add_importlibs(disk classpnp ntoskrnl hal) +add_cd_file(TARGET disk DESTINATION reactos/system32/drivers NO_CAB FOR all) +add_registry_inf(disk_reg.inf) +add_driver_inf(disk disk.inf) diff --git a/drivers/storage/class/disk_new/data.c b/drivers/storage/class/disk_new/data.c new file mode 100644 index 00000000000..97214055bb1 --- /dev/null +++ b/drivers/storage/class/disk_new/data.c @@ -0,0 +1,88 @@ +/*++ + +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("PAGE") +#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 } +}; + +// ======== ROS DIFF ======== +// Added MediaTypes in their own brace nesting level +// ======== ROS DIFF ======== + +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/drivers/storage/class/disk_new/disk.c b/drivers/storage/class/disk_new/disk.c new file mode 100644 index 00000000000..29259da6dba --- /dev/null +++ b/drivers/storage/class/disk_new/disk.c @@ -0,0 +1,6195 @@ +/*++ + +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskDriverReinit( + IN PDRIVER_OBJECT DriverObject, + IN PVOID Nothing, + IN ULONG Count + ) +{ + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(Nothing); + UNREFERENCED_PARAMETER(Count); + + DiskIsPastReinit = TRUE; +} + +VOID +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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. + // + +#ifdef _MSC_VER +#pragma prefast(suppress:28175); +#endif + 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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; + NTSTATUS SyncCacheStatus = STATUS_SUCCESS; + + // + // 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")); + + SyncCacheStatus = 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, (PVOID)SyncCacheStatus, 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 - If disk had write cache enabled and SYNC CACHE command was sent as 1st part of FLUSH processing + then context must carry the completion status of SYNC CACHE request, + else context must be set to STATUS_SUCCESS. + +Return Value: + + STATUS_SUCCESS if successful, an error code otherwise + +--*/ + +{ + PDISK_GROUP_CONTEXT FlushContext; + NTSTATUS status; + PFUNCTIONAL_DEVICE_EXTENSION fdoExt; + PDISK_DATA diskData; +#ifdef _MSC_VER + #pragma warning(suppress:4311) // pointer truncation from 'PVOID' to 'NTSTATUS' +#endif + NTSTATUS SyncCacheStatus = (NTSTATUS) 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); + + // + // If sync cache failed earlier, final status of the flush request needs to be failure + // even if SRB_FUNCTION_FLUSH srb request succeeded + // + if (NT_SUCCESS(status) && + (!NT_SUCCESS(SyncCacheStatus))) { + Irp->IoStatus.Status = status = SyncCacheStatus; + } + + // + // 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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/drivers/storage/class/disk_new/disk.h b/drivers/storage/class/disk_new/disk.h new file mode 100644 index 00000000000..3ba2bf2ac15 --- /dev/null +++ b/drivers/storage/class/disk_new/disk.h @@ -0,0 +1,1288 @@ +/*++ + +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 +#include "classpnp.h" + +#include +#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 +#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 + +#ifdef __REACTOS__ +#undef MdlMappingNoExecute +#define MdlMappingNoExecute 0 +#define NonPagedPoolNx NonPagedPool +#define NonPagedPoolNxCacheAligned NonPagedPoolCacheAligned +#undef POOL_NX_ALLOCATION +#define POOL_NX_ALLOCATION 0 +#endif + + +#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; + +#ifdef _MSC_VER +#pragma warning(suppress: 4201) //this is intended to be an unnamed union +#endif + }; + + 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskAddDevice( + IN PDRIVER_OBJECT DriverObject, + IN PDEVICE_OBJECT Pdo + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskInitFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskStartFdo( + IN PDEVICE_OBJECT Fdo + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskStopDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskRemoveDevice( + IN PDEVICE_OBJECT DeviceObject, + IN UCHAR Type + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskReadWriteVerification( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoProcessError( + PDEVICE_OBJECT DeviceObject, + PSCSI_REQUEST_BLOCK Srb, + NTSTATUS *Status, + BOOLEAN *Retry + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskShutdownFlush( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskGetCacheInformation( + IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, + IN PDISK_CACHE_INFORMATION CacheInfo + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoQueryWmiRegInfo( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoQueryWmiRegInfoEx( + IN PDEVICE_OBJECT DeviceObject, + OUT ULONG *RegFlags, + OUT PUNICODE_STRING InstanceName, + OUT PUNICODE_STRING MofName + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoQueryWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferAvail, + OUT PUCHAR Buffer + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoSetWmiDataBlock( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoSetWmiDataItem( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG DataItemId, + IN ULONG BufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +DiskFdoExecuteWmiMethod( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN ULONG GuidIndex, + IN ULONG MethodId, + IN ULONG InBufferSize, + IN ULONG OutBufferSize, + IN PUCHAR Buffer + ); + +NTSTATUS +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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/drivers/storage/class/disk_new/disk.inf b/drivers/storage/class/disk_new/disk.inf new file mode 100644 index 00000000000..810f95e5793 --- /dev/null +++ b/drivers/storage/class/disk_new/disk.inf @@ -0,0 +1,129 @@ +; DISK.INF + +; Installation of DISK DRIVESs + +[Version] +Signature = "$Windows NT$" +;Signature = "$ReactOS$" +LayoutFile = layout.inf +Class = DiskDrive +ClassGUID = {4D36E967-E325-11CE-BFC1-08002BE10318} +Provider = %MSFT% +; Git commit 3428c5f @ https://github.com/microsoft/Windows-driver-samples +DriverVer = 08/29/2018, 1.0 + +[DestinationDirs] +disk_CopyFiles.NT = 12 +storprop_CopyFiles.NT = 11 + +[ClassInstall32.NT] +AddReg = DiskClass.NT.AddReg +Copyfiles = storprop_CopyFiles.NT + +[DiskClass.NT.AddReg] +HKR, , , 0, %DiskClassName% +HKR, , Installer32, 0, "storprop.dll,DiskClassInstaller" +HKR, , Icon, 0, "-53" +HKR, , NoInstallClass, 0, 1 +HKR, , SilentInstall, 0, 1 + +[Manufacturer] +%GenericMfg% = GenericMfg + +[GenericMfg] +%Disk.DeviceDesc% = disk_Inst,GenDisk +%OpticalDisk.DeviceDesc% = disk_Inst,GenOptical + +;----------------------------- DISK DRIVER ----------------------------- + +[disk_Inst.NT] +CopyFiles = disk_CopyFiles.NT + +[disk_Inst.NT.Services] +AddService = disk, 2, disk_Service_Inst + +[disk_Service_Inst] +DisplayName = %Disk.ServiceDesc% +ServiceType = 1 +StartType = 0 +ErrorControl = 1 +ServiceBinary = %12%\disk.sys + +[disk_CopyFiles.NT] +disk.sys + +;---------------------------- DISK INSTALLER ---------------------------- + +[storprop_CopyFiles.NT] +storprop.dll + +;-------------------------------- STRINGS ------------------------------- + +[Strings] +ReactOS = "ReactOS Team" +GenericMfg = "(Generic Disk Drives)" +DiskClassName = "Disk Drives" +Disk.DeviceDesc = "Disk Drive" +OpticalDisk.DeviceDesc = "Optical Disk Drive" +Disk.ServiceDesc = "Disk Driver" + +[Strings.0405] +GenericMfg = "(Obecné diskové jednotky)" +DiskClassName = "Diskové jednotky" +Disk.DeviceDesc = "Disková jednotka" +OpticalDisk.DeviceDesc = "Optická disková jednotka" +Disk.ServiceDesc = "Ovladač disku" + +[Strings.0a] +ReactOS = "Equipo de ReactOS" +GenericMfg = "(Unidades de disco genéricas)" +DiskClassName = "Unidades de disco" +Disk.DeviceDesc = "Unidad de disco" +OpticalDisk.DeviceDesc = "Unidad de disco óptico" +Disk.ServiceDesc = "Controlador de disco" + +[Strings.0415] +ReactOS = "Zespół ReactOS" +GenericMfg = "(Standardowy dysk twardy)" +DiskClassName = "Dyski twarde" +Disk.DeviceDesc = "Dysk twardy" +OpticalDisk.DeviceDesc = "Dysk optyczny" +Disk.ServiceDesc = "Sterownik dysku" + +[Strings.0418] +ReactOS = "Echipa ReactOS" +GenericMfg = "(dispozitiv disc generic)" +DiskClassName = "Dispozitive disc" +Disk.DeviceDesc = "Dispozitiv disc" +OpticalDisk.DeviceDesc = "Dispozitiv disc optic" +Disk.ServiceDesc = "Modúl-pilot de disc" + +[Strings.0419] +ReactOS = "Команда ReactOS" +GenericMfg = "(Стандартные дисковые накопители)" +DiskClassName = "Дисковые устройства" +Disk.DeviceDesc = "Дисковое устройство" +OpticalDisk.DeviceDesc = "Оптическое дисковое устройство" +Disk.ServiceDesc = "Драйвер диска" + +[Strings.041f] +ReactOS = "ReactOS Takımı" +GenericMfg = "(Umûmî Disk Sürücüleri)" +DiskClassName = "Disk Sürücüleri" +Disk.DeviceDesc = "Disk Sürücüsü" +OpticalDisk.DeviceDesc = "Optik Disk Sürücüsü" +Disk.ServiceDesc = "Disk Sürücüsü" + +[Strings.0422] +ReactOS = "Команда ReactOS" +GenericMfg = "(Стандартні приводи дисків)" +DiskClassName = "Дискові приводи" +Disk.DeviceDesc = "Дисковий привід" +OpticalDisk.DeviceDesc = "Оптичний привід дисків" +Disk.ServiceDesc = "Драйвер диску" + +[Strings.0804] +GenericMfg = "(通用磁盘驱动器)" +Disk.DeviceDesc = "磁盘驱动器" +OpticalDisk.DeviceDesc = "光盘驱动器" +Disk.ServiceDesc = "磁盘驱动器" diff --git a/drivers/storage/class/disk_new/disk.rc b/drivers/storage/class/disk_new/disk.rc new file mode 100644 index 00000000000..b697f15bd31 --- /dev/null +++ b/drivers/storage/class/disk_new/disk.rc @@ -0,0 +1,23 @@ +/* ------------------------------------------------------------------------- * + * * + * Copyright (c) Microsoft Corporation * + * * + * Module Name : disk.rc * + * * + * Abstract : Resource script for disk.sys * + * * + * ------------------------------------------------------------------------- */ + +#include + +#include + +#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/drivers/storage/class/disk_new/disk_reg.inf b/drivers/storage/class/disk_new/disk_reg.inf new file mode 100644 index 00000000000..0e265c0e333 --- /dev/null +++ b/drivers/storage/class/disk_new/disk_reg.inf @@ -0,0 +1,7 @@ +; Disk class driver +[AddReg] +HKLM,"SYSTEM\CurrentControlSet\Services\Disk","ErrorControl",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\Disk","Group",0x00000000,"SCSI Class" +HKLM,"SYSTEM\CurrentControlSet\Services\Disk","ImagePath",0x00020000,"system32\drivers\disk.sys" +HKLM,"SYSTEM\CurrentControlSet\Services\Disk","Start",0x00010001,0x00000000 +HKLM,"SYSTEM\CurrentControlSet\Services\Disk","Type",0x00010001,0x00000001 diff --git a/drivers/storage/class/disk_new/diskwmi.c b/drivers/storage/class/disk_new/diskwmi.c new file mode 100644 index 00000000000..449d3e16b38 --- /dev/null +++ b/drivers/storage/class/disk_new/diskwmi.c @@ -0,0 +1,3510 @@ +/*++ + +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 + 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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/drivers/storage/class/disk_new/geometry.c b/drivers/storage/class/disk_new/geometry.c new file mode 100644 index 00000000000..91eab0e7f8e --- /dev/null +++ b/drivers/storage/class/disk_new/geometry.c @@ -0,0 +1,1676 @@ +/*++ + +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++) { +#ifdef _MSC_VER +#pragma warning(suppress: 6386) // PREFast bug means it doesn't correctly remember the size of DetectInfoList +#endif + 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. + // + unmatchedDiskCount = 0; + for(deviceObject = DriverObject->DeviceObject; + deviceObject != NULL; +#ifdef _MSC_VER +#pragma prefast(suppress:28175, "Need to access the opaque field to scan through the list of disks") +#endif + 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/drivers/storage/class/disk_new/license.txt b/drivers/storage/class/disk_new/license.txt new file mode 100644 index 00000000000..6ef3ee38d1c --- /dev/null +++ b/drivers/storage/class/disk_new/license.txt @@ -0,0 +1,23 @@ +The Microsoft Public License (MS-PL) +Copyright (c) 2015 Microsoft + +This license governs use of the accompanying software. If you use the software, you + accept this license. If you do not accept the license, do not use the software. + +1. Definitions + The terms "reproduce," "reproduction," "derivative works," and "distribution" have the + same meaning here as under U.S. copyright law. + A "contribution" is the original software, or any additions or changes to the software. + A "contributor" is any person that distributes its contribution under this license. + "Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/drivers/storage/class/disk_new/pnp.c b/drivers/storage/class/disk_new/pnp.c new file mode 100644 index 00000000000..2023a00abb2 --- /dev/null +++ b/drivers/storage/class/disk_new/pnp.c @@ -0,0 +1,1108 @@ +/*++ + +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 + +#ifndef __REACTOS__ +extern PULONG InitSafeBootMode; +#else +extern NTSYSAPI ULONG InitSafeBootMode; +#endif +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 | RTL_QUERY_REGISTRY_TYPECHECK; + queryTable[0].Name = L"RootPartitionMountable"; + queryTable[0].EntryContext = &(rootPartitionMountable); + queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE; + +#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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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, + (PVOID *)&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) // __REACTOS__ + { + 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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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 +NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ +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() +