3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 SCSI disk class driver - this module contains all the code for generating
29 #include "geometry.tmh"
32 #if defined(_X86_) || defined(_AMD64_)
36 IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension
40 DiskUpdateRemovableGeometry (
41 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
45 DiskScanBusDetectInfo(
46 IN PDRIVER_OBJECT DriverObject
,
51 DiskSaveBusDetectInfo(
52 IN PDRIVER_OBJECT DriverObject
,
58 DiskSaveGeometryDetectInfo(
59 IN PDRIVER_OBJECT DriverObject
,
65 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
66 OUT PDISK_GEOMETRY Geometry
69 typedef struct _DISK_DETECT_INFO
{
74 PDEVICE_OBJECT Device
;
75 CM_INT13_DRIVE_PARAMETER DriveParameters
;
76 } DISK_DETECT_INFO
, *PDISK_DETECT_INFO
;
79 // Information about the disk geometries collected and saved into the registry
80 // by NTDETECT.COM or the system firmware.
83 PDISK_DETECT_INFO DetectInfoList
= NULL
;
84 ULONG DetectInfoCount
= 0;
85 LONG DetectInfoUsedCount
= 0;
87 #define GET_STARTING_SECTOR(p) ( \
88 (ULONG) (p->StartingSectorLsb0) + \
89 (ULONG) (p->StartingSectorLsb1 << 8 ) + \
90 (ULONG) (p->StartingSectorMsb0 << 16) + \
91 (ULONG) (p->StartingSectorMsb1 << 24) )
93 #define GET_ENDING_S_OF_CHS(p) ( \
94 (UCHAR) (p->EndingCylinderLsb & 0x3F) )
97 // Definitions from hal.h
101 // Boot record disk partition table entry structure format
104 typedef struct _PARTITION_DESCRIPTOR
108 UCHAR StartingCylinderLsb
;
109 UCHAR StartingCylinderMsb
;
112 UCHAR EndingCylinderLsb
;
113 UCHAR EndingCylinderMsb
;
114 UCHAR StartingSectorLsb0
;
115 UCHAR StartingSectorLsb1
;
116 UCHAR StartingSectorMsb0
;
117 UCHAR StartingSectorMsb1
;
118 UCHAR PartitionLengthLsb0
;
119 UCHAR PartitionLengthLsb1
;
120 UCHAR PartitionLengthMsb0
;
121 UCHAR PartitionLengthMsb1
;
123 } PARTITION_DESCRIPTOR
, *PPARTITION_DESCRIPTOR
;
126 // Number of partition table entries
129 #define NUM_PARTITION_TABLE_ENTRIES 4
132 // Partition table record and boot signature offsets in 16-bit words
135 #define PARTITION_TABLE_OFFSET ( 0x1be / 2)
136 #define BOOT_SIGNATURE_OFFSET ((0x200 / 2) - 1)
139 // Boot record signature value
142 #define BOOT_RECORD_SIGNATURE (0xaa55)
146 #pragma alloc_text(INIT, DiskSaveDetectInfo)
147 #pragma alloc_text(INIT, DiskScanBusDetectInfo)
148 #pragma alloc_text(INIT, DiskSaveBusDetectInfo)
149 #pragma alloc_text(INIT, DiskSaveGeometryDetectInfo)
151 #pragma alloc_text(PAGE, DiskUpdateGeometry)
152 #pragma alloc_text(PAGE, DiskUpdateRemovableGeometry)
153 #pragma alloc_text(PAGE, DiskGetPortGeometry)
154 #pragma alloc_text(PAGE, DiskIsNT4Geometry)
155 #pragma alloc_text(PAGE, DiskGetDetectInfo)
156 #pragma alloc_text(PAGE, DiskReadSignature)
162 PDRIVER_OBJECT DriverObject
169 This routine saves away the firmware information about the disks which has
170 been saved in the registry. It generates a list (DetectInfoList) which
171 contains the disk geometries, signatures & checksums of all drives which
172 were examined by NtDetect. This list is later used to assign geometries
173 to disks as they are initialized.
177 DriverObject - the driver being initialized. This is used to get to the
187 OBJECT_ATTRIBUTES objectAttributes
= {0};
190 UNICODE_STRING unicodeString
;
197 InitializeObjectAttributes(
199 DriverObject
->HardwareDatabase
,
200 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
205 // Create the hardware base key.
208 status
= ZwOpenKey(&hardwareKey
, KEY_READ
, &objectAttributes
);
210 if(!NT_SUCCESS(status
)) {
211 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveDetectInfo: Cannot open hardware data. "
213 DriverObject
->HardwareDatabase
));
217 status
= DiskSaveGeometryDetectInfo(DriverObject
, hardwareKey
);
219 if(!NT_SUCCESS(status
)) {
220 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveDetectInfo: Can't query configuration data "
223 ZwClose(hardwareKey
);
228 // Open EISA bus key.
231 RtlInitUnicodeString(&unicodeString
, L
"EisaAdapter");
232 InitializeObjectAttributes(&objectAttributes
,
234 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
238 status
= ZwOpenKey(&busKey
,
242 if(NT_SUCCESS(status
)) {
243 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskSaveDetectInfo: Opened EisaAdapter key\n"));
244 DiskScanBusDetectInfo(DriverObject
, busKey
);
249 // Open MultiFunction bus key.
252 RtlInitUnicodeString(&unicodeString
, L
"MultifunctionAdapter");
253 InitializeObjectAttributes(&objectAttributes
,
255 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
259 status
= ZwOpenKey(&busKey
,
263 if(NT_SUCCESS(status
)) {
264 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n"));
265 DiskScanBusDetectInfo(DriverObject
, busKey
);
269 ZwClose(hardwareKey
);
271 return STATUS_SUCCESS
;
276 DiskCleanupDetectInfo(
277 IN PDRIVER_OBJECT DriverObject
283 This routine will cleanup the data structure built by DiskSaveDetectInfo.
287 DriverObject - a pointer to the kernel object for this driver.
296 UNREFERENCED_PARAMETER(DriverObject
);
297 FREE_POOL(DetectInfoList
);
303 DiskSaveGeometryDetectInfo(
304 IN PDRIVER_OBJECT DriverObject
,
305 IN HANDLE HardwareKey
308 UNICODE_STRING unicodeString
;
309 PKEY_VALUE_FULL_INFORMATION keyData
;
312 PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor
;
313 PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor
;
315 PCM_INT13_DRIVE_PARAMETER driveParameters
;
316 ULONG numberOfDrives
;
323 UNREFERENCED_PARAMETER(DriverObject
);
326 // Get disk BIOS geometry information.
329 RtlInitUnicodeString(&unicodeString
, L
"Configuration Data");
331 keyData
= ExAllocatePoolWithTag(PagedPool
,
333 DISK_TAG_UPDATE_GEOM
);
335 if(keyData
== NULL
) {
336 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveGeometryDetectInfo: Can't allocate config "
338 return STATUS_INSUFFICIENT_RESOURCES
;
341 status
= ZwQueryValueKey(HardwareKey
,
343 KeyValueFullInformation
,
348 if(!NT_SUCCESS(status
)) {
349 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveGeometryDetectInfo: Can't query configuration "
357 // Extract the resource list out of the key data.
360 fullDescriptor
= (PCM_FULL_RESOURCE_DESCRIPTOR
)
361 (((PUCHAR
) keyData
) + keyData
->DataOffset
);
363 fullDescriptor
->PartialResourceList
.PartialDescriptors
;
364 length
= partialDescriptor
->u
.DeviceSpecificData
.DataSize
;
366 if((keyData
->DataLength
< sizeof(CM_FULL_RESOURCE_DESCRIPTOR
)) ||
367 (fullDescriptor
->PartialResourceList
.Count
== 0) ||
368 (partialDescriptor
->Type
!= CmResourceTypeDeviceSpecific
) ||
369 (length
< sizeof(ULONG
))) {
371 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveGeometryDetectInfo: BIOS header data too small "
374 return STATUS_INVALID_PARAMETER
;
378 // Point to the BIOS data. THe BIOS data is located after the first
379 // partial Resource list which should be device specific data.
383 PUCHAR buffer
= (PUCHAR
) keyData
;
384 buffer
+= keyData
->DataOffset
;
385 buffer
+= sizeof(CM_FULL_RESOURCE_DESCRIPTOR
);
386 driveParameters
= (PCM_INT13_DRIVE_PARAMETER
) buffer
;
389 numberOfDrives
= length
/ sizeof(CM_INT13_DRIVE_PARAMETER
);
392 // Allocate our detect info list now that we know how many entries there
393 // are going to be. No other routine allocates detect info and this is
394 // done out of DriverEntry so we don't need to synchronize it's creation.
397 length
= sizeof(DISK_DETECT_INFO
) * numberOfDrives
;
398 DetectInfoList
= ExAllocatePoolWithTag(PagedPool
,
400 DISK_TAG_UPDATE_GEOM
);
402 if(DetectInfoList
== NULL
) {
403 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes "
404 "for DetectInfoList\n",
408 return STATUS_INSUFFICIENT_RESOURCES
;
411 DetectInfoCount
= numberOfDrives
;
413 RtlZeroMemory(DetectInfoList
, length
);
416 // Copy the information out of the key data and into the list we've
420 for(i
= 0; i
< numberOfDrives
; i
++) {
422 #pragma warning(suppress: 6386) // PREFast bug means it doesn't correctly remember the size of DetectInfoList
424 DetectInfoList
[i
].DriveParameters
= driveParameters
[i
];
428 return STATUS_SUCCESS
;
433 DiskScanBusDetectInfo(
434 IN PDRIVER_OBJECT DriverObject
,
441 The routine queries the registry to determine which disks are visible to
442 the BIOS. If a disk is visable to the BIOS then the geometry information
443 is updated with the disk's signature and MBR checksum.
447 DriverObject - the object for this driver.
448 BusKey - handle to the bus key to be enumerated.
460 for(busNumber
= 0; ; busNumber
++) {
462 WCHAR buffer
[32] = { 0 };
463 UNICODE_STRING unicodeString
;
465 OBJECT_ATTRIBUTES objectAttributes
= {0};
472 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber
));
475 // Open controller name key.
478 status
= RtlStringCchPrintfW(buffer
, sizeof(buffer
) / sizeof(buffer
[0]) - 1, L
"%d", busNumber
);
479 if (!NT_SUCCESS(status
)) {
480 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status
));
484 RtlInitUnicodeString(&unicodeString
, buffer
);
486 InitializeObjectAttributes(&objectAttributes
,
488 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
492 status
= ZwOpenKey(&spareKey
, KEY_READ
, &objectAttributes
);
494 if(!NT_SUCCESS(status
)) {
495 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Error %#08lx opening bus "
502 // Open up a controller ordinal key.
505 RtlInitUnicodeString(&unicodeString
, L
"DiskController");
506 InitializeObjectAttributes(&objectAttributes
,
508 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
512 status
= ZwOpenKey(&adapterKey
, KEY_READ
, &objectAttributes
);
515 if(!NT_SUCCESS(status
)) {
516 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Error %#08lx opening "
517 "DiskController key\n",
522 for(adapterNumber
= 0; ; adapterNumber
++) {
531 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Scanning disk key "
532 "%d\\DiskController\\%d\\DiskPeripheral\n",
533 busNumber
, adapterNumber
));
535 status
= RtlStringCchPrintfW(buffer
, sizeof(buffer
) / sizeof(buffer
[0]) - 1, L
"%d\\DiskPeripheral", adapterNumber
);
536 if (!NT_SUCCESS(status
)) {
537 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status
));
541 RtlInitUnicodeString(&unicodeString
, buffer
);
543 InitializeObjectAttributes(&objectAttributes
,
545 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
549 status
= ZwOpenKey(&diskKey
, KEY_READ
, &objectAttributes
);
551 if(!NT_SUCCESS(status
)) {
552 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Error %#08lx opening "
558 for(diskNumber
= 0; ; diskNumber
++) {
562 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Scanning target key "
563 "%d\\DiskController\\%d\\DiskPeripheral\\%d\n",
564 busNumber
, adapterNumber
, diskNumber
));
566 status
= RtlStringCchPrintfW(buffer
, sizeof(buffer
) / sizeof(buffer
[0]) - 1, L
"%d", diskNumber
);
567 if (!NT_SUCCESS(status
)) {
568 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Format symbolic link failed with error: 0x%X\n", status
));
572 RtlInitUnicodeString(&unicodeString
, buffer
);
574 InitializeObjectAttributes(&objectAttributes
,
576 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
580 status
= ZwOpenKey(&targetKey
, KEY_READ
, &objectAttributes
);
582 if(!NT_SUCCESS(status
)) {
583 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskScanBusDetectInfo: Error %#08lx "
584 "opening target key\n",
589 DiskSaveBusDetectInfo(DriverObject
, targetKey
, diskNumber
);
605 DiskSaveBusDetectInfo(
606 IN PDRIVER_OBJECT DriverObject
,
614 This routine will transfer the firmware/ntdetect reported information
615 in the specified target key into the appropriate entry in the
620 DriverObject - the object for this driver.
622 TargetKey - the key for the disk being saved.
624 DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this
633 PDISK_DETECT_INFO diskInfo
;
635 UNICODE_STRING unicodeString
;
637 PKEY_VALUE_FULL_INFORMATION keyData
;
643 UNREFERENCED_PARAMETER(DriverObject
);
645 if (DiskNumber
>= DetectInfoCount
)
647 return STATUS_UNSUCCESSFUL
;
650 diskInfo
= &(DetectInfoList
[DiskNumber
]);
652 if(diskInfo
->Initialized
) {
655 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: disk entry %#x already has a "
656 "signature of %#08lx and mbr checksum of %#08lx\n",
659 diskInfo
->MbrCheckSum
));
660 return STATUS_UNSUCCESSFUL
;
663 RtlInitUnicodeString(&unicodeString
, L
"Identifier");
665 keyData
= ExAllocatePoolWithTag(PagedPool
,
667 DISK_TAG_UPDATE_GEOM
);
669 if(keyData
== NULL
) {
670 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: Couldn't allocate space for "
672 return STATUS_INSUFFICIENT_RESOURCES
;
676 // Get disk peripheral identifier.
679 status
= ZwQueryValueKey(TargetKey
,
681 KeyValueFullInformation
,
686 if(!NT_SUCCESS(status
)) {
687 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: Error %#08lx getting "
693 } else if (keyData
->DataLength
< 9*sizeof(WCHAR
)) {
696 // the data is too short to use (we subtract 9 chars in normal path)
698 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: Saved data was invalid, "
699 "not enough data in registry!\n"));
701 return STATUS_UNSUCCESSFUL
;
705 UNICODE_STRING identifier
;
709 // Complete unicode string.
712 identifier
.Buffer
= (PWSTR
) ((PUCHAR
)keyData
+ keyData
->DataOffset
);
713 identifier
.Length
= (USHORT
) keyData
->DataLength
;
714 identifier
.MaximumLength
= (USHORT
) keyData
->DataLength
;
717 // Get the first value out of the identifier - this will be the MBR
721 status
= RtlUnicodeStringToInteger(&identifier
, 16, &value
);
723 if(!NT_SUCCESS(status
)) {
724 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: Error %#08lx converting "
725 "identifier %wZ into MBR xsum\n",
732 diskInfo
->MbrCheckSum
= value
;
735 // Shift the string over to get the disk signature
738 identifier
.Buffer
+= 9;
739 identifier
.Length
-= 9 * sizeof(WCHAR
);
740 identifier
.MaximumLength
-= 9 * sizeof(WCHAR
);
742 status
= RtlUnicodeStringToInteger(&identifier
, 16, &value
);
744 if(!NT_SUCCESS(status
)) {
745 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskSaveBusDetectInfo: Error %#08lx converting "
746 "identifier %wZ into disk signature\n",
752 diskInfo
->Signature
= value
;
756 // Here is where we would save away the extended int13 data.
760 // Mark this entry as initialized so we can make sure not to do it again.
763 diskInfo
->Initialized
= TRUE
;
767 return STATUS_SUCCESS
;
773 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
779 This routine checks the DetectInfoList saved away during disk driver init
780 to see if any geometry information was reported for this drive. If the
781 geometry data exists (determined by matching non-zero signatures or
782 non-zero MBR checksums) then it will be saved in the RealGeometry member
783 of the disk data block.
785 ClassReadDriveCapacity MUST be called after calling this routine to update
786 the cylinder count based on the size of the disk and the presence of any
787 disk management software.
791 DeviceExtension - Supplies a pointer to the device information for disk.
795 Inidicates whether the "RealGeometry" in the data block is now valid.
800 PDISK_DATA diskData
= FdoExtension
->CommonExtension
.DriverData
;
803 PDISK_DETECT_INFO diskInfo
= NULL
;
805 BOOLEAN found
= FALSE
;
812 NT_ASSERT((FdoExtension
->DeviceObject
->Characteristics
& FILE_REMOVABLE_MEDIA
) == 0);
815 // If we've already set a non-default geometry for this drive then there's
816 // no need to try and update again.
819 if(diskData
->GeometrySource
!= DiskGeometryUnknown
) {
820 return diskData
->GeometrySource
;
824 // Scan through the saved detect info to see if we can find a match
828 for(i
= 0; i
< DetectInfoCount
; i
++) {
830 NT_ASSERT(DetectInfoList
!= NULL
);
832 diskInfo
= &(DetectInfoList
[i
]);
834 if((diskData
->Mbr
.Signature
!= 0) &&
835 (diskData
->Mbr
.Signature
== diskInfo
->Signature
)) {
836 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: found match for signature "
838 diskData
->Mbr
.Signature
));
841 } else if((diskData
->Mbr
.Signature
== 0) &&
842 (diskData
->Mbr
.MbrCheckSum
!= 0) &&
843 (diskData
->Mbr
.MbrCheckSum
== diskInfo
->MbrCheckSum
)) {
844 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: found match for xsum %#08lx\n",
845 diskData
->Mbr
.MbrCheckSum
));
854 ULONG sectorsPerTrack
;
855 ULONG tracksPerCylinder
;
860 // Point to the array of drive parameters.
863 cylinders
= diskInfo
->DriveParameters
.MaxCylinders
+ 1;
864 sectorsPerTrack
= diskInfo
->DriveParameters
.SectorsPerTrack
;
865 tracksPerCylinder
= diskInfo
->DriveParameters
.MaxHeads
+ 1;
868 // Since the BIOS may not report the full drive, recalculate the drive
869 // size based on the volume size and the BIOS values for tracks per
870 // cylinder and sectors per track..
873 length
= tracksPerCylinder
* sectorsPerTrack
;
878 // The BIOS information is bogus.
881 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n",
882 tracksPerCylinder
, sectorsPerTrack
));
883 return DiskGeometryUnknown
;
887 // since we are copying the structure RealGeometry here, we should
888 // really initialize all the fields, especially since a zero'd
889 // BytesPerSector field would cause a trap in xHalReadPartitionTable()
892 diskData
->RealGeometry
= FdoExtension
->DiskGeometry
;
895 // Save the geometry information away in the disk data block and
896 // set the bit indicating that we found a valid one.
899 diskData
->RealGeometry
.SectorsPerTrack
= sectorsPerTrack
;
900 diskData
->RealGeometry
.TracksPerCylinder
= tracksPerCylinder
;
901 diskData
->RealGeometry
.Cylinders
.QuadPart
= (LONGLONG
)cylinders
;
903 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, "
905 sectorsPerTrack
, tracksPerCylinder
, cylinders
));
907 diskData
->GeometrySource
= DiskGeometryFromBios
;
908 diskInfo
->Device
= FdoExtension
->DeviceObject
;
911 // Increment the count of used geometry entries.
914 InterlockedIncrement(&DetectInfoUsedCount
);
918 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData
->Mbr
.Signature
));
921 if(diskData
->GeometrySource
== DiskGeometryUnknown
) {
924 // We couldn't find a geometry from the BIOS. Check with the port
925 // driver and see if it can provide one.
928 status
= DiskGetPortGeometry(FdoExtension
, &(diskData
->RealGeometry
));
930 if(NT_SUCCESS(status
)) {
933 // Check the geometry to make sure it's valid.
936 if((diskData
->RealGeometry
.TracksPerCylinder
*
937 diskData
->RealGeometry
.SectorsPerTrack
) != 0) {
939 diskData
->GeometrySource
= DiskGeometryFromPort
;
940 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension
));
942 if (diskData
->RealGeometry
.BytesPerSector
== 0) {
944 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: Port driver failed to "
945 "set BytesPerSector in the RealGeometry\n"));
946 diskData
->RealGeometry
.BytesPerSector
=
947 FdoExtension
->DiskGeometry
.BytesPerSector
;
948 if (diskData
->RealGeometry
.BytesPerSector
== 0) {
949 NT_ASSERT(!"BytesPerSector is still zero!");
958 // If we came up with a "real" geometry for this drive then set it in the
962 if (diskData
->GeometrySource
!= DiskGeometryUnknown
) {
964 FdoExtension
->DiskGeometry
= diskData
->RealGeometry
;
967 return diskData
->GeometrySource
;
972 DiskUpdateRemovableGeometry (
973 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
980 This routine updates the geometry of the disk. It will query the port
981 driver to see if it can provide any geometry info. If not it will use
982 the current head & sector count.
984 Based on these values & the capacity of the drive as reported by
985 ClassReadDriveCapacity it will determine a new cylinder count for the
990 Fdo - Supplies the functional device object whos size needs to be updated.
994 Returns the status of the opertion.
998 PCOMMON_DEVICE_EXTENSION commonExtension
= &(FdoExtension
->CommonExtension
);
999 PDISK_DATA diskData
= commonExtension
->DriverData
;
1000 PDISK_GEOMETRY geometry
= &(diskData
->RealGeometry
);
1006 if (FdoExtension
->DeviceDescriptor
) {
1007 NT_ASSERT(FdoExtension
->DeviceDescriptor
->RemovableMedia
);
1009 NT_ASSERT(TEST_FLAG(FdoExtension
->DeviceObject
->Characteristics
,
1010 FILE_REMOVABLE_MEDIA
));
1013 // Attempt to determine the disk geometry. First we'll check with the
1014 // port driver to see what it suggests for a value.
1017 status
= DiskGetPortGeometry(FdoExtension
, geometry
);
1019 if(NT_SUCCESS(status
) &&
1020 ((geometry
->TracksPerCylinder
* geometry
->SectorsPerTrack
) != 0)) {
1022 FdoExtension
->DiskGeometry
= (*geometry
);
1030 DiskGetPortGeometry(
1031 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1032 OUT PDISK_GEOMETRY Geometry
1036 Routine Description:
1038 This routine will query the port driver for disk geometry. Some port
1039 drivers (in particular IDEPORT) may be able to provide geometry for the
1044 FdoExtension - the device object for the disk.
1046 Geometry - a structure to save the geometry information into (if any is
1051 STATUS_SUCCESS if geometry information can be provided or
1052 error status indicating why it can't.
1056 PCOMMON_DEVICE_EXTENSION commonExtension
= &(FdoExtension
->CommonExtension
);
1058 PIO_STACK_LOCATION irpStack
;
1066 // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver.
1069 irp
= IoAllocateIrp(commonExtension
->LowerDeviceObject
->StackSize
, FALSE
);
1072 return STATUS_INSUFFICIENT_RESOURCES
;
1075 irpStack
= IoGetNextIrpStackLocation(irp
);
1077 irpStack
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
1079 irpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1080 IOCTL_DISK_GET_DRIVE_GEOMETRY
;
1081 irpStack
->Parameters
.DeviceIoControl
.OutputBufferLength
=
1082 sizeof(DISK_GEOMETRY
);
1084 irp
->AssociatedIrp
.SystemBuffer
= Geometry
;
1086 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1088 IoSetCompletionRoutine(irp
,
1089 ClassSignalCompletion
,
1095 status
= IoCallDriver(commonExtension
->LowerDeviceObject
, irp
);
1096 if (status
== STATUS_PENDING
) {
1097 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1098 status
= irp
->IoStatus
.Status
;
1109 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1114 Routine Description:
1116 The default geometry that was used in partitioning disks under Windows NT 4.0 was
1118 Sectors per Track = 0x20 = 32
1119 Tracks per Cylinder = 0x40 = 64
1121 This was changed in Windows 2000 to
1123 Sectors per Track = 0x3F = 63
1124 Tracks per Cylinder = 0xFF = 255
1126 If neither the BIOS nor the port driver can report the correct geometry, we will
1127 default to the new numbers on such disks. Now LVM uses the geometry when creating
1128 logical volumes and dynamic disks. So reporting an incorrect geometry will cause
1129 the entire extended partition / dynamic disk to be destroyed
1131 In this routine, we will look at the Master Boot Record. In 90% of the cases, the
1132 first entry corresponds to a partition that starts on the first track. If this is
1133 so, we shall retrieve the logical block address associated with it and calculate
1134 the correct geometry. Now, all partitions start on a cylinder boundary. So, for
1135 the remaining 10% we will look at the ending CHS number to determine the geometry
1140 PUSHORT readBuffer
= NULL
;
1141 BOOLEAN bFoundNT4
= FALSE
;
1145 readBuffer
= ExAllocatePoolWithTag(NonPagedPoolNx
, FdoExtension
->DiskGeometry
.BytesPerSector
, DISK_TAG_UPDATE_GEOM
);
1150 LARGE_INTEGER diskOffset
;
1151 IO_STATUS_BLOCK ioStatus
= { 0 };
1154 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
1157 // Read the Master Boot Record at disk offset 0
1160 diskOffset
.QuadPart
= 0;
1162 irp
= IoBuildSynchronousFsdRequest(IRP_MJ_READ
, FdoExtension
->DeviceObject
, readBuffer
, FdoExtension
->DiskGeometry
.BytesPerSector
, &diskOffset
, &event
, &ioStatus
);
1166 PIO_STACK_LOCATION irpSp
= IoGetNextIrpStackLocation(irp
);
1169 irpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1171 status
= IoCallDriver(FdoExtension
->DeviceObject
, irp
);
1173 if (status
== STATUS_PENDING
)
1175 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
1176 status
= ioStatus
.Status
;
1179 if (NT_SUCCESS(status
))
1182 // Match the boot record signature
1185 if (readBuffer
[BOOT_SIGNATURE_OFFSET
] == BOOT_RECORD_SIGNATURE
)
1187 PPARTITION_DESCRIPTOR partitionTableEntry
= (PPARTITION_DESCRIPTOR
)&readBuffer
[PARTITION_TABLE_OFFSET
];
1191 // Walk the entries looking for a clue as to what the geometry might be
1194 for (uCount
= 0; uCount
< NUM_PARTITION_TABLE_ENTRIES
; uCount
++)
1197 // We are only concerned if there might be a logical volume or if this disk is part of a dynamic set
1200 if (IsContainerPartition(partitionTableEntry
->PartitionType
) || partitionTableEntry
->PartitionType
== PARTITION_LDM
)
1203 // In 90% of the cases, the first entry corresponds to a partition that starts on the first track
1206 if (partitionTableEntry
->StartingTrack
== 1 && GET_STARTING_SECTOR(partitionTableEntry
) == 0x20)
1213 // In almost every case, the ending CHS number is on a cylinder boundary
1216 if (partitionTableEntry
->EndingTrack
== 0x3F && GET_ENDING_S_OF_CHS(partitionTableEntry
) == 0x20)
1223 partitionTableEntry
++;
1229 // The Master Boot Record is invalid
1235 FREE_POOL(readBuffer
);
1243 DiskReadDriveCapacity(
1244 IN PDEVICE_OBJECT Fdo
1248 Routine Description:
1250 This routine is used by disk.sys as a wrapper for the classpnp API
1251 ClassReadDriveCapacity. It will perform some additional operations to
1252 attempt to determine drive geometry before it calls the classpnp version
1255 For fixed disks this involves calling DiskUpdateGeometry which will check
1256 various sources (the BIOS, the port driver) for geometry information.
1260 Fdo - a pointer to the device object to be checked.
1264 status of ClassReadDriveCapacity.
1269 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
1272 if (TEST_FLAG(Fdo
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
1273 DiskUpdateRemovableGeometry(fdoExtension
);
1275 DiskUpdateGeometry(fdoExtension
);
1278 status
= ClassReadDriveCapacity(Fdo
);
1285 DiskDriverReinitialization(
1286 IN PDRIVER_OBJECT DriverObject
,
1292 Routine Description:
1294 This routine will scan through the current list of disks and attempt to
1295 match them to any remaining geometry information. This will only be done
1296 on the first call to the routine.
1298 Note: This routine assumes that the system will not be adding or removing
1299 devices during this phase of the init process. This is very likely
1300 a bad assumption but it greatly simplifies the code.
1304 DriverObject - a pointer to the object for the disk driver.
1308 Count - an indication of how many times this routine has been called.
1317 PDEVICE_OBJECT deviceObject
;
1318 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
1319 PDISK_DATA diskData
;
1321 ULONG unmatchedDiskCount
;
1322 PDEVICE_OBJECT unmatchedDisk
= NULL
;
1325 PDISK_DETECT_INFO diskInfo
= NULL
;
1327 UNREFERENCED_PARAMETER(Nothing
);
1330 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
, "DiskDriverReinitialization: ignoring call %d\n",
1336 // Check to see how many entries in the detect info list have been matched.
1337 // If there's only one remaining we'll see if we can find a disk to go with
1341 if(DetectInfoCount
== 0) {
1342 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
, "DiskDriverReinitialization: no detect info saved\n"));
1346 if((DetectInfoCount
- DetectInfoUsedCount
) != 1) {
1347 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
, "DiskDriverReinitialization: %d of %d geometry entries "
1348 "used - will not attempt match\n", DetectInfoUsedCount
, DetectInfoCount
));
1353 // Scan through the list of disks and see if any of them are missing
1354 // geometry information. If there is only one such disk we'll try to
1355 // match it to the unmatched geometry.
1360 // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep
1361 // removals from happening while doing this.
1363 unmatchedDiskCount
= 0;
1364 for(deviceObject
= DriverObject
->DeviceObject
;
1365 deviceObject
!= NULL
;
1367 #pragma prefast(suppress:28175, "Need to access the opaque field to scan through the list of disks")
1369 deviceObject
= deviceObject
->NextDevice
) {
1371 fdoExtension
= deviceObject
->DeviceExtension
;
1373 if (!fdoExtension
->CommonExtension
.IsFdo
) {
1374 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: %#p is not an FDO\n",
1380 // If the geometry for this one is already known then skip it.
1383 diskData
= fdoExtension
->CommonExtension
.DriverData
;
1384 if(diskData
->GeometrySource
!= DiskGeometryUnknown
) {
1385 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: FDO %#p has a geometry\n",
1390 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: FDO %#p has no geometry\n",
1394 // Mark this one as using the default. It's past the time when disk
1395 // might blunder across the geometry info. If we set the geometry
1396 // from the bios we'll reset this field down below.
1399 diskData
->GeometrySource
= DiskGeometryFromDefault
;
1402 // As long as we've only got one unmatched disk we're fine.
1405 unmatchedDiskCount
++;
1406 if(unmatchedDiskCount
> 1) {
1407 NT_ASSERT(unmatchedDisk
!= NULL
);
1408 TracePrint((TRACE_LEVEL_WARNING
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: FDO %#p also has no geometry\n",
1410 unmatchedDisk
= NULL
;
1414 unmatchedDisk
= deviceObject
;
1418 // If there's more or less than one ungeometried disk then we can't do
1419 // anything about the geometry.
1422 if(unmatchedDiskCount
!= 1) {
1423 TracePrint((TRACE_LEVEL_ERROR
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: Unable to match geometry\n"));
1428 fdoExtension
= unmatchedDisk
->DeviceExtension
;
1429 diskData
= fdoExtension
->CommonExtension
.DriverData
;
1431 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: Found possible match\n"));
1434 // Find the geometry which wasn't assigned.
1437 for(i
= 0; i
< DetectInfoCount
; i
++) {
1438 if(DetectInfoList
[i
].Device
== NULL
) {
1439 diskInfo
= &(DetectInfoList
[i
]);
1444 if (diskInfo
!= NULL
)
1447 // Save the geometry information away in the disk data block and
1448 // set the bit indicating that we found a valid one.
1452 ULONG sectorsPerTrack
;
1453 ULONG tracksPerCylinder
;
1458 // Point to the array of drive parameters.
1461 cylinders
= diskInfo
->DriveParameters
.MaxCylinders
+ 1;
1462 sectorsPerTrack
= diskInfo
->DriveParameters
.SectorsPerTrack
;
1463 tracksPerCylinder
= diskInfo
->DriveParameters
.MaxHeads
+ 1;
1466 // Since the BIOS may not report the full drive, recalculate the drive
1467 // size based on the volume size and the BIOS values for tracks per
1468 // cylinder and sectors per track..
1471 length
= tracksPerCylinder
* sectorsPerTrack
;
1476 // The BIOS information is bogus.
1479 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: H (%d) or S(%d) is zero\n",
1480 tracksPerCylinder
, sectorsPerTrack
));
1485 // since we are copying the structure RealGeometry here, we should
1486 // really initialize all the fields, especially since a zero'd
1487 // BytesPerSector field would cause a trap in xHalReadPartitionTable()
1490 diskData
->RealGeometry
= fdoExtension
->DiskGeometry
;
1493 // Save the geometry information away in the disk data block and
1494 // set the bit indicating that we found a valid one.
1497 diskData
->RealGeometry
.SectorsPerTrack
= sectorsPerTrack
;
1498 diskData
->RealGeometry
.TracksPerCylinder
= tracksPerCylinder
;
1499 diskData
->RealGeometry
.Cylinders
.QuadPart
= (LONGLONG
)cylinders
;
1501 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskDriverReinit: BIOS spt %#x, #heads %#x, "
1503 sectorsPerTrack
, tracksPerCylinder
, cylinders
));
1505 diskData
->GeometrySource
= DiskGeometryGuessedFromBios
;
1506 diskInfo
->Device
= unmatchedDisk
;
1509 // Now copy the geometry over to the fdo extension and call
1510 // classpnp to redetermine the disk size and cylinder count.
1513 fdoExtension
->DiskGeometry
= diskData
->RealGeometry
;
1515 (VOID
)ClassReadDriveCapacity(unmatchedDisk
);
1517 if (diskData
->RealGeometry
.BytesPerSector
== 0) {
1520 // if the BytesPerSector field is set to zero for a disk
1521 // listed in the bios, then the system will bugcheck in
1522 // xHalReadPartitionTable(). assert here since it is
1523 // easier to determine what is happening this way.
1526 NT_ASSERT(!"RealGeometry not set to non-zero bps\n");
1536 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1537 OUT PDISK_DETECTION_INFO DetectInfo
1541 Routine Description:
1543 Get the Int13 information from the BIOS DetectInfoList.
1547 FdoExtension - Supplies a pointer to the FDO extension that we want to
1548 obtain the detect information for.
1550 DetectInfo - A buffer where the detect information will be copied to.
1559 BOOLEAN found
= FALSE
;
1560 PDISK_DETECT_INFO diskInfo
= NULL
;
1561 PDISK_DATA diskData
= FdoExtension
->CommonExtension
.DriverData
;
1566 // Fail for non-fixed drives.
1569 if (TEST_FLAG (FdoExtension
->DeviceObject
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
1570 return STATUS_NOT_SUPPORTED
;
1574 // There is no GPT detection info, so fail this.
1577 if (diskData
->PartitionStyle
== PARTITION_STYLE_GPT
) {
1578 return STATUS_NOT_SUPPORTED
;
1581 for(i
= 0; i
< DetectInfoCount
; i
++) {
1584 NT_ASSERT(DetectInfoList
!= NULL
);
1586 diskInfo
= &(DetectInfoList
[i
]);
1588 if((diskData
->Mbr
.Signature
!= 0) &&
1589 (diskData
->Mbr
.Signature
== diskInfo
->Signature
)) {
1590 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskGetDetectInfo: found match for signature "
1592 diskData
->Mbr
.Signature
));
1595 } else if((diskData
->Mbr
.Signature
== 0) &&
1596 (diskData
->Mbr
.MbrCheckSum
!= 0) &&
1597 (diskData
->Mbr
.MbrCheckSum
== diskInfo
->MbrCheckSum
)) {
1598 TracePrint((TRACE_LEVEL_INFORMATION
, TRACE_FLAG_GENERAL
, "DiskGetDetectInfo: found match for xsum %#08lx\n",
1599 diskData
->Mbr
.MbrCheckSum
));
1606 DetectInfo
->DetectionType
= DetectInt13
;
1607 DetectInfo
->Int13
.DriveSelect
= diskInfo
->DriveParameters
.DriveSelect
;
1608 DetectInfo
->Int13
.MaxCylinders
= diskInfo
->DriveParameters
.MaxCylinders
;
1609 DetectInfo
->Int13
.SectorsPerTrack
= diskInfo
->DriveParameters
.SectorsPerTrack
;
1610 DetectInfo
->Int13
.MaxHeads
= diskInfo
->DriveParameters
.MaxHeads
;
1611 DetectInfo
->Int13
.NumberDrives
= diskInfo
->DriveParameters
.NumberDrives
;
1612 RtlZeroMemory (&DetectInfo
->ExInt13
, sizeof (DetectInfo
->ExInt13
));
1615 return (found
? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
);
1621 IN PDEVICE_OBJECT Fdo
1626 Routine Description:
1628 Read the disks signature from the drive. The signature can be either
1629 a MBR signature or a GPT/EFI signature.
1631 The low-level signature reading is done by IoReadDiskSignature().
1635 Fdo - Pointer to the FDO of a disk to read the signature for.
1646 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= Fdo
->DeviceExtension
;
1647 PDISK_DATA diskData
= fdoExtension
->CommonExtension
.DriverData
;
1648 DISK_SIGNATURE Signature
= { 0 };
1652 Status
= IoReadDiskSignature (Fdo
,
1653 fdoExtension
->DiskGeometry
.BytesPerSector
,
1656 if (!NT_SUCCESS (Status
)) {
1660 if (Signature
.PartitionStyle
== PARTITION_STYLE_GPT
) {
1661 diskData
->PartitionStyle
= PARTITION_STYLE_GPT
;
1662 diskData
->Efi
.DiskId
= Signature
.Gpt
.DiskId
;
1663 } else if (Signature
.PartitionStyle
== PARTITION_STYLE_MBR
) {
1664 diskData
->PartitionStyle
= PARTITION_STYLE_MBR
;
1665 diskData
->Mbr
.Signature
= Signature
.Mbr
.Signature
;
1666 diskData
->Mbr
.MbrCheckSum
= Signature
.Mbr
.CheckSum
;
1669 Status
= STATUS_UNSUCCESSFUL
;
1675 #endif // defined(_X86_) || defined(_AMD64_)