/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 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" #if defined(JAPAN) && defined(_X86_) #include "machine.h" #endif #include #if defined(_X86_) #include "mountdev.h" #endif #ifdef ExAllocatePool #undef ExAllocatePool #define ExAllocatePool #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 typedef VOID (*PDISK_UPDATE_PARTITIONS) ( IN PDEVICE_OBJECT Fdo, IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList ); #if defined(_X86_) // // Disk device data // typedef enum _DISK_GEOMETRY_SOURCE { DiskGeometryUnknown, DiskGeometryFromBios, DiskGeometryFromPort, DiskGeometryFromNec98, DiskGeometryGuessedFromBios, DiskGeometryFromDefault } DISK_GEOMETRY_SOURCE, *PDISK_GEOMETRY_SOURCE; #endif // 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; }; // unnamed union struct { // // This flag is set when the well known name is created (through // DiskCreateSymbolicLinks) and cleared when destroying it // (by calling DiskDeleteSymbolicLinks). // BOOLEAN WellKnownNameCreated : 1; // // This flag is set when the PhysicalDriveN link is created (through // DiskCreateSymbolicLinks) and is cleared when destroying it (through // DiskDeleteSymbolicLinks) // BOOLEAN 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; // // Routine to be called when updating the disk partitions. This routine // is different for removable and non-removable media and is called by // (among other things) DiskEnumerateDevice // PDISK_UPDATE_PARTITIONS UpdatePartitionRoutine; // // SCSI address used for SMART operations. // SCSI_ADDRESS ScsiAddress; // // Event used to synchronize partitioning operations and enumerations. // KEVENT PartitioningEvent; // // These unicode strings hold the disk and volume interface strings. If // the interfaces were not registered or could not be set then the string // buffer will be NULL. // UNICODE_STRING DiskInterfaceString; UNICODE_STRING PartitionInterfaceString; // // What type of failure prediction mechanism is available // FAILURE_PREDICTION_METHOD FailurePredictionCapability; BOOLEAN AllowFPPerfHit; #if defined(_X86_) // // 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 // // Indicates that the cached partition table is valid when set. // ULONG CachedPartitionTableValid; // // The cached partition table - this is only valid if the previous // flag is set. When invalidated the cached partition table will be // freed and replaced the next time one of the partitioning functions is // called. This allows the error handling routines to invalidate it by // setting the flag and doesn't require that they obtain a lock. // PDRIVE_LAYOUT_INFORMATION_EX CachedPartitionTable; // // 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; } 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 HackDisableWriteCacheNotSupported (0x40) #define DiskDeviceParameterSubkey L"Disk" #define DiskDeviceSpecialFlags L"SpecialFlags" #define DiskDeviceUserWriteCacheSetting L"UserWriteCacheSetting" #define FUNCTIONAL_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(DISK_DATA) #define PHYSICAL_EXTENSION_SIZE sizeof(PHYSICAL_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 { PCHAR VendorId; PCHAR ProductId; PCHAR 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; // // 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; #define MAX_SECTORS_PER_VERIFY 0x200 // // 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 // // Static global lookup tables. // extern CLASSPNP_SCAN_FOR_SPECIAL_INFO DiskBadControllers[]; extern const DISK_MEDIA_TYPES_LIST DiskMediaTypes[]; // // Macros // // // Routine prototypes. // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); VOID DiskUnload( IN PDRIVER_OBJECT DriverObject ); NTSTATUS DiskAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ); NTSTATUS DiskInitFdo( IN PDEVICE_OBJECT Fdo ); NTSTATUS DiskInitPdo( IN PDEVICE_OBJECT Pdo ); NTSTATUS DiskStartFdo( IN PDEVICE_OBJECT Fdo ); NTSTATUS DiskStartPdo( IN PDEVICE_OBJECT Pdo ); NTSTATUS DiskStopDevice( IN PDEVICE_OBJECT DeviceObject, IN UCHAR Type ); NTSTATUS DiskRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN UCHAR Type ); NTSTATUS DiskReadWriteVerification( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS DiskDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID DiskFdoProcessError( PDEVICE_OBJECT DeviceObject, PSCSI_REQUEST_BLOCK Srb, NTSTATUS *Status, BOOLEAN *Retry ); NTSTATUS DiskShutdownFlush( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS DiskGetCacheInformation( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PDISK_CACHE_INFORMATION CacheInfo ); NTSTATUS DiskSetCacheInformation( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PDISK_CACHE_INFORMATION CacheInfo ); VOID DiskLogCacheInformation( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN PDISK_CACHE_INFORMATION CacheInfo, IN NTSTATUS Status ); VOID DisableWriteCache( IN PDEVICE_OBJECT DeviceObject, IN PIO_WORKITEM WorkItem ); VOID DiskIoctlVerify( IN PDEVICE_OBJECT DeviceObject, IN PDISK_VERIFY_WORKITEM_CONTEXT Context ); NTSTATUS DiskModeSelect( IN PDEVICE_OBJECT DeviceObject, IN 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 ); NTSTATUS DiskDetectFailurePrediction( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, PFAILURE_PREDICTION_METHOD FailurePredictCapability ); BOOLEAN EnumerateBusKey( IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension, HANDLE BusKey, PULONG DiskNumber ); NTSTATUS DiskCreateFdo( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT LowerDeviceObject, IN PULONG DeviceCount, IN BOOLEAN DasdAccessOnly ); VOID UpdateDeviceObjects( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID DiskSetSpecialHacks( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, IN ULONG_PTR Data ); VOID DiskScanRegistryForSpecial( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ); VOID ResetBus( IN PDEVICE_OBJECT DeviceObject ); NTSTATUS DiskEnumerateDevice( IN PDEVICE_OBJECT Fdo ); NTSTATUS DiskQueryId( IN PDEVICE_OBJECT Pdo, IN BUS_QUERY_ID_TYPE IdType, IN PUNICODE_STRING UnicodeIdString ); NTSTATUS DiskQueryPnpCapabilities( IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_CAPABILITIES Capabilities ); NTSTATUS DiskGenerateDeviceName( IN BOOLEAN IsFdo, IN ULONG DeviceNumber, IN OPTIONAL ULONG PartitionNumber, IN OPTIONAL PLARGE_INTEGER StartingOffset, IN OPTIONAL PLARGE_INTEGER PartitionLength, OUT PUCHAR *RawName ); VOID DiskCreateSymbolicLinks( IN PDEVICE_OBJECT DeviceObject ); VOID DiskUpdatePartitions( IN PDEVICE_OBJECT Fdo, IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList ); VOID DiskUpdateRemovablePartitions( IN PDEVICE_OBJECT Fdo, IN OUT PDRIVE_LAYOUT_INFORMATION_EX PartitionList ); NTSTATUS DiskCreatePdo( IN PDEVICE_OBJECT Fdo, IN ULONG PartitionOrdinal, IN PPARTITION_INFORMATION_EX PartitionEntry, IN PARTITION_STYLE PartitionStyle, OUT PDEVICE_OBJECT *Pdo ); VOID DiskDeleteSymbolicLinks( IN PDEVICE_OBJECT DeviceObject ); NTSTATUS DiskPdoQueryWmiRegInfo( IN PDEVICE_OBJECT DeviceObject, OUT ULONG *RegFlags, OUT PUNICODE_STRING InstanceName ); NTSTATUS DiskPdoQueryWmiDataBlock( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG BufferAvail, OUT PUCHAR Buffer ); NTSTATUS DiskPdoSetWmiDataBlock( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG BufferSize, IN PUCHAR Buffer ); NTSTATUS DiskPdoSetWmiDataItem( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG DataItemId, IN ULONG BufferSize, IN PUCHAR Buffer ); NTSTATUS DiskPdoExecuteWmiMethod( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG MethodId, IN ULONG InBufferSize, IN ULONG OutBufferSize, IN PUCHAR Buffer ); NTSTATUS DiskFdoQueryWmiRegInfo( IN PDEVICE_OBJECT DeviceObject, OUT ULONG *RegFlags, OUT PUNICODE_STRING InstanceName ); NTSTATUS DiskFdoQueryWmiRegInfoEx( IN PDEVICE_OBJECT DeviceObject, OUT ULONG *RegFlags, OUT PUNICODE_STRING InstanceName, OUT PUNICODE_STRING MofName ); NTSTATUS DiskFdoQueryWmiDataBlock( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG BufferAvail, OUT PUCHAR Buffer ); NTSTATUS DiskFdoSetWmiDataBlock( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG BufferSize, IN PUCHAR Buffer ); NTSTATUS DiskFdoSetWmiDataItem( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG DataItemId, IN ULONG BufferSize, IN PUCHAR Buffer ); NTSTATUS DiskFdoExecuteWmiMethod( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN ULONG MethodId, IN ULONG InBufferSize, IN ULONG OutBufferSize, IN PUCHAR Buffer ); NTSTATUS DiskWmiFunctionControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN ULONG GuidIndex, IN CLASSENABLEDISABLEFUNCTION Function, IN BOOLEAN Enable ); NTSTATUS DiskReadFailurePredictStatus( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus ); NTSTATUS DiskReadFailurePredictData( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData ); NTSTATUS DiskEnableDisableFailurePrediction( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, BOOLEAN Enable ); NTSTATUS DiskEnableDisableFailurePredictPolling( PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, BOOLEAN Enable, ULONG PollTimeInSeconds ); VOID DiskAcquirePartitioningLock( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ); VOID DiskReleasePartitioningLock( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension ); NTSTATUS DiskInitializeReregistration( void ); extern GUIDREGINFO DiskWmiFdoGuidList[]; extern GUIDREGINFO DiskWmiPdoGuidList[]; #if defined(_X86_) NTSTATUS DiskReadDriveCapacity( IN PDEVICE_OBJECT Fdo ); #else #define DiskReadDriveCapacity(Fdo) ClassReadDriveCapacity(Fdo) #endif #if defined(_X86_) #if 0 NTSTATUS DiskQuerySuggestedLinkName( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); #endif NTSTATUS DiskSaveDetectInfo( PDRIVER_OBJECT DriverObject ); VOID DiskCleanupDetectInfo( IN PDRIVER_OBJECT DriverObject ); VOID DiskDriverReinitialization ( IN PDRIVER_OBJECT DriverObject, IN PVOID Nothing, IN ULONG Count ); #endif VOID DiskConvertPartitionToExtended( IN PPARTITION_INFORMATION Partition, OUT PPARTITION_INFORMATION_EX PartitionEx ); PDRIVE_LAYOUT_INFORMATION_EX DiskConvertLayoutToExtended( IN CONST PDRIVE_LAYOUT_INFORMATION Layout ); PDRIVE_LAYOUT_INFORMATION DiskConvertExtendedToLayout( IN CONST PDRIVE_LAYOUT_INFORMATION_EX LayoutEx ); NTSTATUS DiskReadPartitionTableEx( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN BOOLEAN BypassCache, OUT PDRIVE_LAYOUT_INFORMATION_EX* DriveLayout ); NTSTATUS DiskWritePartitionTableEx( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN PDRIVE_LAYOUT_INFORMATION_EX DriveLayout ); NTSTATUS DiskSetPartitionInformationEx( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN ULONG PartitionNumber, IN struct _SET_PARTITION_INFORMATION_EX* PartitionInfo ); NTSTATUS DiskSetPartitionInformation( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN ULONG SectorSize, IN ULONG PartitionNumber, IN ULONG PartitionType ); NTSTATUS DiskVerifyPartitionTable( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN BOOLEAN FixErrors ); BOOLEAN DiskInvalidatePartitionTable( IN PFUNCTIONAL_DEVICE_EXTENSION Fdo, IN BOOLEAN PartitionLockHeld ); #if defined (_X86_) NTSTATUS DiskGetDetectInfo( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, OUT PDISK_DETECTION_INFO DetectInfo ); NTSTATUS DiskReadSignature( IN PDEVICE_OBJECT Fdo ); #else #define DiskGetDetectInfo(FdoExtension, DetectInfo) (STATUS_UNSUCCESSFUL) #endif #define DiskHashGuid(Guid) (((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0] ^ ((PULONG) &Guid)[0])