xref: /reactos/drivers/storage/class/disk/geometry.c (revision 3e1f4074)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4 
5 Module Name:
6 
7     geometry.c
8 
9 Abstract:
10 
11     SCSI disk class driver - this module contains all the code for generating
12     disk geometries.
13 
14 Environment:
15 
16     kernel mode only
17 
18 Notes:
19 
20 Revision History:
21 
22 --*/
23 
24 
25 #include "disk.h"
26 #include "ntddstor.h"
27 
28 #ifdef DEBUG_USE_WPP
29 #include "geometry.tmh"
30 #endif
31 
32 #if defined(_X86_) || defined(_AMD64_)
33 
34 DISK_GEOMETRY_SOURCE
35 DiskUpdateGeometry(
36     IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension
37     );
38 
39 NTSTATUS
40 DiskUpdateRemovableGeometry (
41     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
42     );
43 
44 VOID
45 DiskScanBusDetectInfo(
46     IN PDRIVER_OBJECT DriverObject,
47     IN HANDLE BusKey
48     );
49 
50 NTSTATUS
51 DiskSaveBusDetectInfo(
52     IN PDRIVER_OBJECT DriverObject,
53     IN HANDLE TargetKey,
54     IN ULONG DiskNumber
55     );
56 
57 NTSTATUS
58 DiskSaveGeometryDetectInfo(
59     IN PDRIVER_OBJECT DriverObject,
60     IN HANDLE HardwareKey
61     );
62 
63 NTSTATUS
64 DiskGetPortGeometry(
65     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
66     OUT PDISK_GEOMETRY Geometry
67     );
68 
69 typedef struct _DISK_DETECT_INFO {
70     BOOLEAN Initialized;
71     ULONG Style;
72     ULONG Signature;
73     ULONG MbrCheckSum;
74     PDEVICE_OBJECT Device;
75     CM_INT13_DRIVE_PARAMETER DriveParameters;
76 } DISK_DETECT_INFO, *PDISK_DETECT_INFO;
77 
78 //
79 // Information about the disk geometries collected and saved into the registry
80 // by NTDETECT.COM or the system firmware.
81 //
82 
83 PDISK_DETECT_INFO DetectInfoList = NULL;
84 ULONG DetectInfoCount            = 0;
85 LONG DetectInfoUsedCount        = 0;
86 
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) )
92 
93 #define GET_ENDING_S_OF_CHS(p)                (     \
94         (UCHAR) (p->EndingCylinderLsb & 0x3F) )
95 
96 //
97 // Definitions from hal.h
98 //
99 
100 //
101 // Boot record disk partition table entry structure format
102 //
103 
104 typedef struct _PARTITION_DESCRIPTOR
105 {
106     UCHAR ActiveFlag;
107     UCHAR StartingTrack;
108     UCHAR StartingCylinderLsb;
109     UCHAR StartingCylinderMsb;
110     UCHAR PartitionType;
111     UCHAR EndingTrack;
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;
122 
123 } PARTITION_DESCRIPTOR, *PPARTITION_DESCRIPTOR;
124 
125 //
126 // Number of partition table entries
127 //
128 
129 #define NUM_PARTITION_TABLE_ENTRIES     4
130 
131 //
132 // Partition table record and boot signature offsets in 16-bit words
133 //
134 
135 #define PARTITION_TABLE_OFFSET          ( 0x1be / 2)
136 #define BOOT_SIGNATURE_OFFSET           ((0x200 / 2) - 1)
137 
138 //
139 // Boot record signature value
140 //
141 
142 #define BOOT_RECORD_SIGNATURE           (0xaa55)
143 
144 
145 #ifdef ALLOC_PRAGMA
146 #pragma alloc_text(INIT, DiskSaveDetectInfo)
147 #pragma alloc_text(INIT, DiskScanBusDetectInfo)
148 #pragma alloc_text(INIT, DiskSaveBusDetectInfo)
149 #pragma alloc_text(INIT, DiskSaveGeometryDetectInfo)
150 
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)
157 #endif
158 
159 
160 NTSTATUS
161 DiskSaveDetectInfo(
162     PDRIVER_OBJECT DriverObject
163     )
164 
165 /*++
166 
167 Routine Description:
168 
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.
174 
175 Arguments:
176 
177     DriverObject - the driver being initialized.  This is used to get to the
178                    hardware database.
179 
180 Return Value:
181 
182     status.
183 
184 --*/
185 
186 {
187     OBJECT_ATTRIBUTES objectAttributes = {0};
188     HANDLE hardwareKey;
189 
190     UNICODE_STRING unicodeString;
191     HANDLE busKey;
192 
193     NTSTATUS status;
194 
195     PAGED_CODE();
196 
197     InitializeObjectAttributes(
198         &objectAttributes,
199         DriverObject->HardwareDatabase,
200         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
201         NULL,
202         NULL);
203 
204     //
205     // Create the hardware base key.
206     //
207 
208     status = ZwOpenKey(&hardwareKey, KEY_READ, &objectAttributes);
209 
210     if(!NT_SUCCESS(status)) {
211         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Cannot open hardware data. "
212                        "Name: %wZ\n",
213                     DriverObject->HardwareDatabase));
214         return status;
215     }
216 
217     status = DiskSaveGeometryDetectInfo(DriverObject, hardwareKey);
218 
219     if(!NT_SUCCESS(status)) {
220         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Can't query configuration data "
221                        "(%#08lx)\n",
222                     status));
223         ZwClose(hardwareKey);
224         return status;
225     }
226 
227     //
228     // Open EISA bus key.
229     //
230 
231     RtlInitUnicodeString(&unicodeString, L"EisaAdapter");
232     InitializeObjectAttributes(&objectAttributes,
233                                &unicodeString,
234                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
235                                hardwareKey,
236                                NULL);
237 
238     status = ZwOpenKey(&busKey,
239                        KEY_READ,
240                        &objectAttributes);
241 
242     if(NT_SUCCESS(status)) {
243         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Opened EisaAdapter key\n"));
244         DiskScanBusDetectInfo(DriverObject, busKey);
245         ZwClose(busKey);
246     }
247 
248     //
249     // Open MultiFunction bus key.
250     //
251 
252     RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter");
253     InitializeObjectAttributes(&objectAttributes,
254                                &unicodeString,
255                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
256                                hardwareKey,
257                                NULL);
258 
259     status = ZwOpenKey(&busKey,
260                        KEY_READ,
261                        &objectAttributes);
262 
263     if(NT_SUCCESS(status)) {
264         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n"));
265         DiskScanBusDetectInfo(DriverObject, busKey);
266         ZwClose(busKey);
267     }
268 
269     ZwClose(hardwareKey);
270 
271     return STATUS_SUCCESS;
272 }
273 
274 
275 VOID
276 DiskCleanupDetectInfo(
277     IN PDRIVER_OBJECT DriverObject
278     )
279 /*++
280 
281 Routine Description:
282 
283     This routine will cleanup the data structure built by DiskSaveDetectInfo.
284 
285 Arguments:
286 
287     DriverObject - a pointer to the kernel object for this driver.
288 
289 Return Value:
290 
291     none
292 
293 --*/
294 
295 {
296     UNREFERENCED_PARAMETER(DriverObject);
297     FREE_POOL(DetectInfoList);
298     return;
299 }
300 
301 
302 NTSTATUS
303 DiskSaveGeometryDetectInfo(
304     IN PDRIVER_OBJECT DriverObject,
305     IN HANDLE HardwareKey
306     )
307 {
308     UNICODE_STRING unicodeString;
309     PKEY_VALUE_FULL_INFORMATION keyData;
310     ULONG length;
311 
312     PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor;
313     PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
314 
315     PCM_INT13_DRIVE_PARAMETER driveParameters;
316     ULONG numberOfDrives;
317 
318     ULONG i;
319 
320     NTSTATUS status;
321 
322     PAGED_CODE();
323     UNREFERENCED_PARAMETER(DriverObject);
324 
325     //
326     // Get disk BIOS geometry information.
327     //
328 
329     RtlInitUnicodeString(&unicodeString, L"Configuration Data");
330 
331     keyData = ExAllocatePoolWithTag(PagedPool,
332                                     VALUE_BUFFER_SIZE,
333                                     DISK_TAG_UPDATE_GEOM);
334 
335     if(keyData == NULL) {
336         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Can't allocate config "
337                        "data buffer\n"));
338         return STATUS_INSUFFICIENT_RESOURCES;
339     }
340 
341     status = ZwQueryValueKey(HardwareKey,
342                              &unicodeString,
343                              KeyValueFullInformation,
344                              keyData,
345                              VALUE_BUFFER_SIZE,
346                              &length);
347 
348     if(!NT_SUCCESS(status)) {
349         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Can't query configuration "
350                        "data (%#08lx)\n",
351                     status));
352         FREE_POOL(keyData);
353         return status;
354     }
355 
356     //
357     // Extract the resource list out of the key data.
358     //
359 
360     fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
361                       (((PUCHAR) keyData) + keyData->DataOffset);
362     partialDescriptor =
363         fullDescriptor->PartialResourceList.PartialDescriptors;
364     length = partialDescriptor->u.DeviceSpecificData.DataSize;
365 
366     if((keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) ||
367        (fullDescriptor->PartialResourceList.Count == 0) ||
368        (partialDescriptor->Type != CmResourceTypeDeviceSpecific) ||
369        (length < sizeof(ULONG))) {
370 
371            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: BIOS header data too small "
372                        "or invalid\n"));
373         FREE_POOL(keyData);
374         return STATUS_INVALID_PARAMETER;
375     }
376 
377     //
378     // Point to the BIOS data.  THe BIOS data is located after the first
379     // partial Resource list which should be device specific data.
380     //
381 
382     {
383         PUCHAR buffer = (PUCHAR) keyData;
384         buffer += keyData->DataOffset;
385         buffer += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
386         driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer;
387     }
388 
389     numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER);
390 
391     //
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.
395     //
396 
397     length = sizeof(DISK_DETECT_INFO) * numberOfDrives;
398     DetectInfoList = ExAllocatePoolWithTag(PagedPool,
399                                            length,
400                                            DISK_TAG_UPDATE_GEOM);
401 
402     if(DetectInfoList == NULL) {
403         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes "
404                        "for DetectInfoList\n",
405                     length));
406 
407         FREE_POOL(keyData);
408         return STATUS_INSUFFICIENT_RESOURCES;
409     }
410 
411     DetectInfoCount = numberOfDrives;
412 
413     RtlZeroMemory(DetectInfoList, length);
414 
415     //
416     // Copy the information out of the key data and into the list we've
417     // allocated.
418     //
419 
420     for(i = 0; i < numberOfDrives; i++) {
421 #ifdef _MSC_VER
422 #pragma warning(suppress: 6386) // PREFast bug means it doesn't correctly remember the size of DetectInfoList
423 #endif
424         DetectInfoList[i].DriveParameters = driveParameters[i];
425     }
426 
427     FREE_POOL(keyData);
428     return STATUS_SUCCESS;
429 }
430 
431 
432 VOID
433 DiskScanBusDetectInfo(
434     IN PDRIVER_OBJECT DriverObject,
435     IN HANDLE BusKey
436     )
437 /*++
438 
439 Routine Description:
440 
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.
444 
445 Arguments:
446 
447     DriverObject - the object for this driver.
448     BusKey - handle to the bus key to be enumerated.
449 
450 Return Value:
451 
452     status
453 
454 --*/
455 {
456     ULONG busNumber;
457 
458     NTSTATUS status;
459 
460     for(busNumber = 0; ; busNumber++) {
461 
462         WCHAR buffer[32] = { 0 };
463         UNICODE_STRING unicodeString;
464 
465         OBJECT_ATTRIBUTES objectAttributes = {0};
466 
467         HANDLE spareKey;
468         HANDLE adapterKey;
469 
470         ULONG adapterNumber;
471 
472         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber));
473 
474         //
475         // Open controller name key.
476         //
477 
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));
481             break;
482         }
483 
484         RtlInitUnicodeString(&unicodeString, buffer);
485 
486         InitializeObjectAttributes(&objectAttributes,
487                                    &unicodeString,
488                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
489                                    BusKey,
490                                    NULL);
491 
492         status = ZwOpenKey(&spareKey, KEY_READ, &objectAttributes);
493 
494         if(!NT_SUCCESS(status)) {
495             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening bus "
496                            "key %#x\n",
497                         status, busNumber));
498             break;
499         }
500 
501         //
502         // Open up a controller ordinal key.
503         //
504 
505         RtlInitUnicodeString(&unicodeString, L"DiskController");
506         InitializeObjectAttributes(&objectAttributes,
507                                    &unicodeString,
508                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
509                                    spareKey,
510                                    NULL);
511 
512         status = ZwOpenKey(&adapterKey, KEY_READ, &objectAttributes);
513         ZwClose(spareKey);
514 
515         if(!NT_SUCCESS(status)) {
516             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening "
517                            "DiskController key\n",
518                            status));
519             continue;
520         }
521 
522         for(adapterNumber = 0; ; adapterNumber++) {
523 
524             HANDLE diskKey;
525             ULONG diskNumber;
526 
527             //
528             // Open disk key.
529             //
530 
531             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning disk key "
532                            "%d\\DiskController\\%d\\DiskPeripheral\n",
533                            busNumber, adapterNumber));
534 
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));
538                 break;
539             }
540 
541             RtlInitUnicodeString(&unicodeString, buffer);
542 
543             InitializeObjectAttributes(&objectAttributes,
544                                        &unicodeString,
545                                        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
546                                        adapterKey,
547                                        NULL);
548 
549             status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
550 
551             if(!NT_SUCCESS(status)) {
552                 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx opening "
553                                "disk key\n",
554                                status));
555                 break;
556             }
557 
558             for(diskNumber = 0; ; diskNumber++) {
559 
560                 HANDLE targetKey;
561 
562                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Scanning target key "
563                                "%d\\DiskController\\%d\\DiskPeripheral\\%d\n",
564                                busNumber, adapterNumber, diskNumber));
565 
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));
569                     break;
570                 }
571 
572                 RtlInitUnicodeString(&unicodeString, buffer);
573 
574                 InitializeObjectAttributes(&objectAttributes,
575                                            &unicodeString,
576                                            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
577                                            diskKey,
578                                            NULL);
579 
580                 status = ZwOpenKey(&targetKey, KEY_READ, &objectAttributes);
581 
582                 if(!NT_SUCCESS(status)) {
583                     TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskScanBusDetectInfo: Error %#08lx "
584                                    "opening target key\n",
585                                 status));
586                     break;
587                 }
588 
589                 DiskSaveBusDetectInfo(DriverObject, targetKey, diskNumber);
590 
591                 ZwClose(targetKey);
592             }
593 
594             ZwClose(diskKey);
595         }
596 
597         ZwClose(adapterKey);
598     }
599 
600     return;
601 }
602 
603 
604 NTSTATUS
605 DiskSaveBusDetectInfo(
606     IN PDRIVER_OBJECT DriverObject,
607     IN HANDLE TargetKey,
608     IN ULONG DiskNumber
609     )
610 /*++
611 
612 Routine Description:
613 
614     This routine will transfer the firmware/ntdetect reported information
615     in the specified target key into the appropriate entry in the
616     DetectInfoList.
617 
618 Arguments:
619 
620     DriverObject - the object for this driver.
621 
622     TargetKey - the key for the disk being saved.
623 
624     DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this
625                  entry
626 
627 Return Value:
628 
629     status
630 
631 --*/
632 {
633     PDISK_DETECT_INFO diskInfo;
634 
635     UNICODE_STRING unicodeString;
636 
637     PKEY_VALUE_FULL_INFORMATION keyData;
638     ULONG length;
639 
640     NTSTATUS status;
641 
642     PAGED_CODE();
643     UNREFERENCED_PARAMETER(DriverObject);
644 
645     if (DiskNumber >= DetectInfoCount)
646     {
647         return STATUS_UNSUCCESSFUL;
648     }
649 
650     diskInfo = &(DetectInfoList[DiskNumber]);
651 
652     if(diskInfo->Initialized) {
653 
654         NT_ASSERT(FALSE);
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",
657                         DiskNumber,
658                         diskInfo->Signature,
659                         diskInfo->MbrCheckSum));
660         return STATUS_UNSUCCESSFUL;
661     }
662 
663     RtlInitUnicodeString(&unicodeString, L"Identifier");
664 
665     keyData = ExAllocatePoolWithTag(PagedPool,
666                                     VALUE_BUFFER_SIZE,
667                                     DISK_TAG_UPDATE_GEOM);
668 
669     if(keyData == NULL) {
670         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Couldn't allocate space for "
671                        "registry data\n"));
672         return STATUS_INSUFFICIENT_RESOURCES;
673     }
674 
675     //
676     // Get disk peripheral identifier.
677     //
678 
679     status = ZwQueryValueKey(TargetKey,
680                              &unicodeString,
681                              KeyValueFullInformation,
682                              keyData,
683                              VALUE_BUFFER_SIZE,
684                              &length);
685 
686     if(!NT_SUCCESS(status)) {
687         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx getting "
688                        "Identifier\n",
689                     status));
690         FREE_POOL(keyData);
691         return status;
692 
693     } else if (keyData->DataLength < 9*sizeof(WCHAR)) {
694 
695         //
696         // the data is too short to use (we subtract 9 chars in normal path)
697         //
698         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Saved data was invalid, "
699                     "not enough data in registry!\n"));
700         FREE_POOL(keyData);
701         return STATUS_UNSUCCESSFUL;
702 
703     } else {
704 
705         UNICODE_STRING identifier;
706         ULONG value;
707 
708         //
709         // Complete unicode string.
710         //
711 
712         identifier.Buffer = (PWSTR) ((PUCHAR)keyData + keyData->DataOffset);
713         identifier.Length = (USHORT) keyData->DataLength;
714         identifier.MaximumLength = (USHORT) keyData->DataLength;
715 
716         //
717         // Get the first value out of the identifier - this will be the MBR
718         // checksum.
719         //
720 
721         status = RtlUnicodeStringToInteger(&identifier, 16, &value);
722 
723         if(!NT_SUCCESS(status)) {
724             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx converting "
725                            "identifier %wZ into MBR xsum\n",
726                            status,
727                            &identifier));
728             FREE_POOL(keyData);
729             return status;
730         }
731 
732         diskInfo->MbrCheckSum = value;
733 
734         //
735         // Shift the string over to get the disk signature
736         //
737 
738         identifier.Buffer += 9;
739         identifier.Length -= 9 * sizeof(WCHAR);
740         identifier.MaximumLength -= 9 * sizeof(WCHAR);
741 
742         status = RtlUnicodeStringToInteger(&identifier, 16, &value);
743 
744         if(!NT_SUCCESS(status)) {
745             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskSaveBusDetectInfo: Error %#08lx converting "
746                            "identifier %wZ into disk signature\n",
747                            status,
748                            &identifier));
749             value = 0;
750         }
751 
752         diskInfo->Signature = value;
753     }
754 
755     //
756     // Here is where we would save away the extended int13 data.
757     //
758 
759     //
760     // Mark this entry as initialized so we can make sure not to do it again.
761     //
762 
763     diskInfo->Initialized = TRUE;
764 
765     FREE_POOL(keyData);
766 
767     return STATUS_SUCCESS;
768 }
769 
770 
771 DISK_GEOMETRY_SOURCE
772 DiskUpdateGeometry(
773     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
774     )
775 /*++
776 
777 Routine Description:
778 
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.
784 
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.
788 
789 Arguments:
790 
791     DeviceExtension - Supplies a pointer to the device information for disk.
792 
793 Return Value:
794 
795     Inidicates whether the "RealGeometry" in the data block is now valid.
796 
797 --*/
798 
799 {
800     PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
801 
802     ULONG i;
803     PDISK_DETECT_INFO diskInfo = NULL;
804 
805     BOOLEAN found = FALSE;
806 
807     NTSTATUS status;
808 
809     PAGED_CODE();
810 
811 
812     NT_ASSERT((FdoExtension->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0);
813 
814     //
815     // If we've already set a non-default geometry for this drive then there's
816     // no need to try and update again.
817     //
818 
819     if(diskData->GeometrySource != DiskGeometryUnknown) {
820         return diskData->GeometrySource;
821     }
822 
823     //
824     // Scan through the saved detect info to see if we can find a match
825     // for this device.
826     //
827 
828     for(i = 0; i < DetectInfoCount; i++) {
829 
830         NT_ASSERT(DetectInfoList != NULL);
831 
832         diskInfo = &(DetectInfoList[i]);
833 
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 "
837                            "%#08lx\n",
838                         diskData->Mbr.Signature));
839             found = TRUE;
840             break;
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));
846             found = TRUE;
847             break;
848         }
849     }
850 
851     if(found) {
852 
853         ULONG cylinders;
854         ULONG sectorsPerTrack;
855         ULONG tracksPerCylinder;
856 
857         ULONG length;
858 
859         //
860         // Point to the array of drive parameters.
861         //
862 
863         cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
864         sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
865         tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
866 
867         //
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..
871         //
872 
873         length = tracksPerCylinder * sectorsPerTrack;
874 
875         if (length == 0) {
876 
877             //
878             // The BIOS information is bogus.
879             //
880 
881             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n",
882                         tracksPerCylinder, sectorsPerTrack));
883             return DiskGeometryUnknown;
884         }
885 
886         //
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()
890         //
891 
892         diskData->RealGeometry = FdoExtension->DiskGeometry;
893 
894         //
895         // Save the geometry information away in the disk data block and
896         // set the bit indicating that we found a valid one.
897         //
898 
899         diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
900         diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
901         diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
902 
903         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, "
904                        "#cylinders %#x\n",
905                    sectorsPerTrack, tracksPerCylinder, cylinders));
906 
907         diskData->GeometrySource = DiskGeometryFromBios;
908         diskInfo->Device = FdoExtension->DeviceObject;
909 
910         //
911         // Increment the count of used geometry entries.
912         //
913 
914         InterlockedIncrement(&DetectInfoUsedCount);
915 
916     } else {
917 
918         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData->Mbr.Signature));
919     }
920 
921     if(diskData->GeometrySource == DiskGeometryUnknown) {
922 
923         //
924         // We couldn't find a geometry from the BIOS.  Check with the port
925         // driver and see if it can provide one.
926         //
927 
928         status = DiskGetPortGeometry(FdoExtension, &(diskData->RealGeometry));
929 
930         if(NT_SUCCESS(status)) {
931 
932             //
933             // Check the geometry to make sure it's valid.
934             //
935 
936             if((diskData->RealGeometry.TracksPerCylinder *
937                 diskData->RealGeometry.SectorsPerTrack) != 0) {
938 
939                 diskData->GeometrySource = DiskGeometryFromPort;
940                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension));
941 
942                 if (diskData->RealGeometry.BytesPerSector == 0) {
943 
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!");
950                     }
951 
952                 }
953             }
954         }
955     }
956 
957     //
958     // If we came up with a "real" geometry for this drive then set it in the
959     // device extension.
960     //
961 
962     if (diskData->GeometrySource != DiskGeometryUnknown) {
963 
964         FdoExtension->DiskGeometry = diskData->RealGeometry;
965     }
966 
967     return diskData->GeometrySource;
968 }
969 
970 
971 NTSTATUS
972 DiskUpdateRemovableGeometry (
973     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
974     )
975 
976 /*++
977 
978 Routine Description:
979 
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.
983 
984     Based on these values & the capacity of the drive as reported by
985     ClassReadDriveCapacity it will determine a new cylinder count for the
986     device.
987 
988 Arguments:
989 
990     Fdo - Supplies the functional device object whos size needs to be updated.
991 
992 Return Value:
993 
994     Returns the status of the opertion.
995 
996 --*/
997 {
998     PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
999     PDISK_DATA diskData = commonExtension->DriverData;
1000     PDISK_GEOMETRY geometry = &(diskData->RealGeometry);
1001 
1002     NTSTATUS status;
1003 
1004     PAGED_CODE();
1005 
1006     if (FdoExtension->DeviceDescriptor) {
1007         NT_ASSERT(FdoExtension->DeviceDescriptor->RemovableMedia);
1008     }
1009     NT_ASSERT(TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
1010                      FILE_REMOVABLE_MEDIA));
1011 
1012     //
1013     // Attempt to determine the disk geometry.  First we'll check with the
1014     // port driver to see what it suggests for a value.
1015     //
1016 
1017     status = DiskGetPortGeometry(FdoExtension, geometry);
1018 
1019     if(NT_SUCCESS(status) &&
1020        ((geometry->TracksPerCylinder * geometry->SectorsPerTrack) != 0)) {
1021 
1022         FdoExtension->DiskGeometry = (*geometry);
1023     }
1024 
1025     return status;
1026 }
1027 
1028 
1029 NTSTATUS
1030 DiskGetPortGeometry(
1031     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1032     OUT PDISK_GEOMETRY Geometry
1033     )
1034 /*++
1035 
1036 Routine Description:
1037 
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
1040     device.
1041 
1042 Arguments:
1043 
1044     FdoExtension - the device object for the disk.
1045 
1046     Geometry - a structure to save the geometry information into (if any is
1047                available)
1048 
1049 Return Value:
1050 
1051     STATUS_SUCCESS if geometry information can be provided or
1052     error status indicating why it can't.
1053 
1054 --*/
1055 {
1056     PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
1057     PIRP irp;
1058     PIO_STACK_LOCATION irpStack;
1059     KEVENT event;
1060 
1061     NTSTATUS status;
1062 
1063     PAGED_CODE();
1064 
1065     //
1066     // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver.
1067     //
1068 
1069     irp = IoAllocateIrp(commonExtension->LowerDeviceObject->StackSize, FALSE);
1070 
1071     if(irp == NULL) {
1072         return STATUS_INSUFFICIENT_RESOURCES;
1073     }
1074 
1075     irpStack = IoGetNextIrpStackLocation(irp);
1076 
1077     irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
1078 
1079     irpStack->Parameters.DeviceIoControl.IoControlCode =
1080         IOCTL_DISK_GET_DRIVE_GEOMETRY;
1081     irpStack->Parameters.DeviceIoControl.OutputBufferLength =
1082         sizeof(DISK_GEOMETRY);
1083 
1084     irp->AssociatedIrp.SystemBuffer = Geometry;
1085 
1086     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1087 
1088     IoSetCompletionRoutine(irp,
1089                            ClassSignalCompletion,
1090                            &event,
1091                            TRUE,
1092                            TRUE,
1093                            TRUE);
1094 
1095     status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
1096     if (status == STATUS_PENDING) {
1097         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1098         status = irp->IoStatus.Status;
1099     }
1100 
1101     IoFreeIrp(irp);
1102 
1103     return status;
1104 }
1105 
1106 
1107 BOOLEAN
1108 DiskIsNT4Geometry(
1109     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1110     )
1111 
1112 /*++
1113 
1114 Routine Description:
1115 
1116     The default geometry that was used in partitioning disks under Windows NT 4.0 was
1117 
1118     Sectors per Track   = 0x20 =  32
1119     Tracks per Cylinder = 0x40 =  64
1120 
1121     This was changed in Windows 2000 to
1122 
1123     Sectors per Track   = 0x3F =  63
1124     Tracks per Cylinder = 0xFF = 255
1125 
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
1130 
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
1136 
1137 --*/
1138 
1139 {
1140     PUSHORT readBuffer = NULL;
1141     BOOLEAN bFoundNT4 = FALSE;
1142 
1143     PAGED_CODE();
1144 
1145     readBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, FdoExtension->DiskGeometry.BytesPerSector, DISK_TAG_UPDATE_GEOM);
1146 
1147     if (readBuffer)
1148     {
1149         KEVENT event;
1150         LARGE_INTEGER diskOffset;
1151         IO_STATUS_BLOCK ioStatus = { 0 };
1152         PIRP irp;
1153 
1154         KeInitializeEvent(&event, NotificationEvent, FALSE);
1155 
1156         //
1157         // Read the Master Boot Record at disk offset 0
1158         //
1159 
1160         diskOffset.QuadPart = 0;
1161 
1162         irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, FdoExtension->DeviceObject, readBuffer, FdoExtension->DiskGeometry.BytesPerSector, &diskOffset, &event, &ioStatus);
1163 
1164         if (irp)
1165         {
1166             PIO_STACK_LOCATION irpSp = IoGetNextIrpStackLocation(irp);
1167             NTSTATUS status;
1168 
1169             irpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
1170 
1171             status = IoCallDriver(FdoExtension->DeviceObject, irp);
1172 
1173             if (status == STATUS_PENDING)
1174             {
1175                 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1176                 status = ioStatus.Status;
1177             }
1178 
1179             if (NT_SUCCESS(status))
1180             {
1181                 //
1182                 // Match the boot record signature
1183                 //
1184 
1185                 if (readBuffer[BOOT_SIGNATURE_OFFSET] == BOOT_RECORD_SIGNATURE)
1186                 {
1187                     PPARTITION_DESCRIPTOR partitionTableEntry = (PPARTITION_DESCRIPTOR)&readBuffer[PARTITION_TABLE_OFFSET];
1188                     ULONG uCount = 0;
1189 
1190                     //
1191                     // Walk the entries looking for a clue as to what the geometry might be
1192                     //
1193 
1194                     for (uCount = 0; uCount < NUM_PARTITION_TABLE_ENTRIES; uCount++)
1195                     {
1196                         //
1197                         // We are only concerned if there might be a logical volume or if this disk is part of a dynamic set
1198                         //
1199 
1200                         if (IsContainerPartition(partitionTableEntry->PartitionType) || partitionTableEntry->PartitionType == PARTITION_LDM)
1201                         {
1202                             //
1203                             // In 90% of the cases, the first entry corresponds to a partition that starts on the first track
1204                             //
1205 
1206                             if (partitionTableEntry->StartingTrack == 1 && GET_STARTING_SECTOR(partitionTableEntry) == 0x20)
1207                             {
1208                                 bFoundNT4 = TRUE;
1209                                 break;
1210                             }
1211 
1212                             //
1213                             // In almost every case, the ending CHS number is on a cylinder boundary
1214                             //
1215 
1216                             if (partitionTableEntry->EndingTrack == 0x3F && GET_ENDING_S_OF_CHS(partitionTableEntry) == 0x20)
1217                             {
1218                                 bFoundNT4 = TRUE;
1219                                 break;
1220                             }
1221                         }
1222 
1223                         partitionTableEntry++;
1224                     }
1225                 }
1226                 else
1227                 {
1228                     //
1229                     // The Master Boot Record is invalid
1230                     //
1231                 }
1232             }
1233         }
1234 
1235         FREE_POOL(readBuffer);
1236     }
1237 
1238     return bFoundNT4;
1239 }
1240 
1241 
1242 NTSTATUS
1243 DiskReadDriveCapacity(
1244     IN PDEVICE_OBJECT Fdo
1245     )
1246 /*++
1247 
1248 Routine Description:
1249 
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
1253     of the routine.
1254 
1255     For fixed disks this involves calling DiskUpdateGeometry which will check
1256     various sources (the BIOS, the port driver) for geometry information.
1257 
1258 Arguments:
1259 
1260     Fdo - a pointer to the device object to be checked.
1261 
1262 Return Value:
1263 
1264     status of ClassReadDriveCapacity.
1265 
1266 --*/
1267 
1268 {
1269     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
1270     NTSTATUS status;
1271 
1272     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
1273         DiskUpdateRemovableGeometry(fdoExtension);
1274     } else {
1275         DiskUpdateGeometry(fdoExtension);
1276     }
1277 
1278     status = ClassReadDriveCapacity(Fdo);
1279 
1280     return status;
1281 }
1282 
1283 
1284 VOID
1285 DiskDriverReinitialization(
1286     IN PDRIVER_OBJECT DriverObject,
1287     IN PVOID Nothing,
1288     IN ULONG Count
1289     )
1290 /*++
1291 
1292 Routine Description:
1293 
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.
1297 
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.
1301 
1302 Arguments:
1303 
1304     DriverObject - a pointer to the object for the disk driver.
1305 
1306     Nothing - unused
1307 
1308     Count - an indication of how many times this routine has been called.
1309 
1310 Return Value:
1311 
1312     none
1313 
1314 --*/
1315 
1316 {
1317     PDEVICE_OBJECT deviceObject;
1318     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1319     PDISK_DATA diskData;
1320 
1321     ULONG unmatchedDiskCount;
1322     PDEVICE_OBJECT unmatchedDisk = NULL;
1323 
1324     ULONG i;
1325     PDISK_DETECT_INFO diskInfo = NULL;
1326 
1327     UNREFERENCED_PARAMETER(Nothing);
1328 
1329     if(Count != 1) {
1330         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinitialization: ignoring call %d\n",
1331                     Count));
1332         return;
1333     }
1334 
1335     //
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
1338     // it.
1339     //
1340 
1341     if(DetectInfoCount == 0) {
1342         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinitialization: no detect info saved\n"));
1343         return;
1344     }
1345 
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));
1349         return;
1350     }
1351 
1352     //
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.
1356     //
1357 
1358 
1359     //
1360     // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep
1361     //                            removals from happening while doing this.
1362     //
1363     unmatchedDiskCount = 0;
1364     for(deviceObject = DriverObject->DeviceObject;
1365         deviceObject != NULL;
1366 #ifdef _MSC_VER
1367 #pragma prefast(suppress:28175, "Need to access the opaque field to scan through the list of disks")
1368 #endif
1369         deviceObject = deviceObject->NextDevice) {
1370 
1371         fdoExtension = deviceObject->DeviceExtension;
1372 
1373         if (!fdoExtension->CommonExtension.IsFdo) {
1374             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: %#p is not an FDO\n",
1375                            deviceObject));
1376             continue;
1377         }
1378 
1379         //
1380         // If the geometry for this one is already known then skip it.
1381         //
1382 
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",
1386                            deviceObject));
1387             continue;
1388         }
1389 
1390         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "DiskDriverReinit: FDO %#p has no geometry\n",
1391                        deviceObject));
1392 
1393         //
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.
1397         //
1398 
1399         diskData->GeometrySource = DiskGeometryFromDefault;
1400 
1401         //
1402         // As long as we've only got one unmatched disk we're fine.
1403         //
1404 
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",
1409                            unmatchedDisk));
1410             unmatchedDisk = NULL;
1411             break;
1412         }
1413 
1414         unmatchedDisk = deviceObject;
1415     }
1416 
1417     //
1418     // If there's more or less than one ungeometried disk then we can't do
1419     // anything about the geometry.
1420     //
1421 
1422     if(unmatchedDiskCount != 1) {
1423         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskDriverReinit: Unable to match geometry\n"));
1424         return;
1425 
1426     }
1427 
1428     fdoExtension = unmatchedDisk->DeviceExtension;
1429     diskData = fdoExtension->CommonExtension.DriverData;
1430 
1431     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: Found possible match\n"));
1432 
1433     //
1434     // Find the geometry which wasn't assigned.
1435     //
1436 
1437     for(i = 0; i < DetectInfoCount; i++) {
1438         if(DetectInfoList[i].Device == NULL) {
1439             diskInfo = &(DetectInfoList[i]);
1440             break;
1441         }
1442     }
1443 
1444     if (diskInfo != NULL)
1445     {
1446         //
1447         // Save the geometry information away in the disk data block and
1448         // set the bit indicating that we found a valid one.
1449         //
1450 
1451         ULONG cylinders;
1452         ULONG sectorsPerTrack;
1453         ULONG tracksPerCylinder;
1454 
1455         ULONG length;
1456 
1457         //
1458         // Point to the array of drive parameters.
1459         //
1460 
1461         cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
1462         sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
1463         tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
1464 
1465         //
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..
1469         //
1470 
1471         length = tracksPerCylinder * sectorsPerTrack;
1472 
1473         if (length == 0) {
1474 
1475             //
1476             // The BIOS information is bogus.
1477             //
1478 
1479             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: H (%d) or S(%d) is zero\n",
1480                         tracksPerCylinder, sectorsPerTrack));
1481             return;
1482         }
1483 
1484         //
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()
1488         //
1489 
1490         diskData->RealGeometry = fdoExtension->DiskGeometry;
1491 
1492         //
1493         // Save the geometry information away in the disk data block and
1494         // set the bit indicating that we found a valid one.
1495         //
1496 
1497         diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
1498         diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
1499         diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
1500 
1501         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskDriverReinit: BIOS spt %#x, #heads %#x, "
1502                        "#cylinders %#x\n",
1503                    sectorsPerTrack, tracksPerCylinder, cylinders));
1504 
1505         diskData->GeometrySource = DiskGeometryGuessedFromBios;
1506         diskInfo->Device = unmatchedDisk;
1507 
1508         //
1509         // Now copy the geometry over to the fdo extension and call
1510         // classpnp to redetermine the disk size and cylinder count.
1511         //
1512 
1513         fdoExtension->DiskGeometry = diskData->RealGeometry;
1514 
1515         (VOID)ClassReadDriveCapacity(unmatchedDisk);
1516 
1517         if (diskData->RealGeometry.BytesPerSector == 0) {
1518 
1519             //
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.
1524             //
1525 
1526             NT_ASSERT(!"RealGeometry not set to non-zero bps\n");
1527         }
1528     }
1529 
1530     return;
1531 }
1532 
1533 
1534 NTSTATUS
1535 DiskGetDetectInfo(
1536     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1537     OUT PDISK_DETECTION_INFO DetectInfo
1538     )
1539 /*++
1540 
1541 Routine Description:
1542 
1543     Get the Int13 information from the BIOS DetectInfoList.
1544 
1545 Arguments:
1546 
1547     FdoExtension - Supplies a pointer to the FDO extension that we want to
1548             obtain the detect information for.
1549 
1550     DetectInfo - A buffer where the detect information will be copied to.
1551 
1552 Return Value:
1553 
1554     NTSTATUS code.
1555 
1556 --*/
1557 {
1558     ULONG i;
1559     BOOLEAN found = FALSE;
1560     PDISK_DETECT_INFO diskInfo = NULL;
1561     PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
1562 
1563     PAGED_CODE ();
1564 
1565     //
1566     // Fail for non-fixed drives.
1567     //
1568 
1569     if (TEST_FLAG (FdoExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
1570         return STATUS_NOT_SUPPORTED;
1571     }
1572 
1573     //
1574     // There is no GPT detection info, so fail this.
1575     //
1576 
1577     if (diskData->PartitionStyle == PARTITION_STYLE_GPT) {
1578         return STATUS_NOT_SUPPORTED;
1579     }
1580 
1581     for(i = 0; i < DetectInfoCount; i++) {
1582 
1583 
1584         NT_ASSERT(DetectInfoList != NULL);
1585 
1586         diskInfo = &(DetectInfoList[i]);
1587 
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 "
1591                            "%#08lx\n",
1592                         diskData->Mbr.Signature));
1593             found = TRUE;
1594             break;
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));
1600             found = TRUE;
1601             break;
1602         }
1603     }
1604 
1605     if ( found ) {
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));
1613     }
1614 
1615     return (found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
1616 }
1617 
1618 
1619 NTSTATUS
1620 DiskReadSignature(
1621     IN PDEVICE_OBJECT Fdo
1622     )
1623 
1624 /*++
1625 
1626 Routine Description:
1627 
1628     Read the disks signature from the drive. The signature can be either
1629     a MBR signature or a GPT/EFI signature.
1630 
1631     The low-level signature reading is done by IoReadDiskSignature().
1632 
1633 Arguments:
1634 
1635     Fdo - Pointer to the FDO of a disk to read the signature for.
1636 
1637 Return Value:
1638 
1639     NTSTATUS code.
1640 
1641 --*/
1642 
1643 
1644 {
1645     NTSTATUS Status;
1646     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
1647     PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
1648     DISK_SIGNATURE Signature = { 0 };
1649 
1650     PAGED_CODE();
1651 
1652     Status = IoReadDiskSignature (Fdo,
1653                                   fdoExtension->DiskGeometry.BytesPerSector,
1654                                   &Signature);
1655 
1656     if (!NT_SUCCESS (Status)) {
1657         return Status;
1658     }
1659 
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;
1667     } else {
1668         NT_ASSERT (FALSE);
1669         Status = STATUS_UNSUCCESSFUL;
1670     }
1671 
1672     return Status;
1673 }
1674 
1675 #endif // defined(_X86_) || defined(_AMD64_)
1676 
1677