6e1b47dc00fc762cfbe54bd26b1ed0ec0bd8824b
[reactos.git] / reactos / drivers / storage / class / disk_new / geometry.c
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 #if defined (_X86_)
29
30 DISK_GEOMETRY_SOURCE
31 NTAPI
32 DiskUpdateGeometry(
33 IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension
34 );
35
36 NTSTATUS
37 NTAPI
38 DiskUpdateRemovableGeometry (
39 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
40 );
41
42 VOID
43 NTAPI
44 DiskScanBusDetectInfo(
45 IN PDRIVER_OBJECT DriverObject,
46 IN HANDLE BusKey
47 );
48
49 NTSTATUS
50 NTAPI
51 DiskSaveBusDetectInfo(
52 IN PDRIVER_OBJECT DriverObject,
53 IN HANDLE TargetKey,
54 IN ULONG DiskNumber
55 );
56
57 NTSTATUS
58 NTAPI
59 DiskSaveGeometryDetectInfo(
60 IN PDRIVER_OBJECT DriverObject,
61 IN HANDLE HardwareKey
62 );
63
64 NTSTATUS
65 NTAPI
66 DiskGetPortGeometry(
67 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
68 OUT PDISK_GEOMETRY Geometry
69 );
70
71 typedef struct _DISK_DETECT_INFO {
72 BOOLEAN Initialized;
73 ULONG Style;
74 ULONG Signature;
75 ULONG MbrCheckSum;
76 PDEVICE_OBJECT Device;
77 CM_INT13_DRIVE_PARAMETER DriveParameters;
78 } DISK_DETECT_INFO, *PDISK_DETECT_INFO;
79
80 //
81 // Information about the disk geometries collected and saved into the registry
82 // by NTDETECT.COM or the system firmware.
83 //
84
85 PDISK_DETECT_INFO DetectInfoList = NULL;
86 ULONG DetectInfoCount = 0;
87 ULONG DetectInfoUsedCount = 0;
88
89 #ifdef ALLOC_PRAGMA
90 #pragma alloc_text(INIT, DiskSaveDetectInfo)
91 #pragma alloc_text(INIT, DiskScanBusDetectInfo)
92 #pragma alloc_text(INIT, DiskSaveBusDetectInfo)
93 #pragma alloc_text(INIT, DiskSaveGeometryDetectInfo)
94
95 #pragma alloc_text(PAGE, DiskUpdateGeometry)
96 #pragma alloc_text(PAGE, DiskUpdateRemovableGeometry)
97 #pragma alloc_text(PAGE, DiskGetPortGeometry)
98 #pragma alloc_text(PAGE, DiskGetDetectInfo)
99 #pragma alloc_text(PAGE, DiskReadSignature)
100 #endif
101
102
103 NTSTATUS
104 NTAPI
105 DiskSaveDetectInfo(
106 PDRIVER_OBJECT DriverObject
107 )
108 /*++
109
110 Routine Description:
111
112 This routine saves away the firmware information about the disks which has
113 been saved in the registry. It generates a list (DetectInfoList) which
114 contains the disk geometries, signatures & checksums of all drives which
115 were examined by NtDetect. This list is later used to assign geometries
116 to disks as they are initialized.
117
118 Arguments:
119
120 DriverObject - the driver being initialized. This is used to get to the
121 hardware database.
122
123 Return Value:
124
125 status.
126
127 --*/
128
129 {
130 OBJECT_ATTRIBUTES objectAttributes;
131 HANDLE hardwareKey;
132
133 UNICODE_STRING unicodeString;
134 HANDLE busKey;
135
136 NTSTATUS status;
137
138 PAGED_CODE();
139
140 InitializeObjectAttributes(
141 &objectAttributes,
142 DriverObject->HardwareDatabase,
143 OBJ_CASE_INSENSITIVE,
144 NULL,
145 NULL);
146
147 //
148 // Create the hardware base key.
149 //
150
151 status = ZwOpenKey(&hardwareKey, KEY_READ, &objectAttributes);
152
153 if(!NT_SUCCESS(status)) {
154 DebugPrint((1, "DiskSaveDetectInfo: Cannot open hardware data. "
155 "Name: %wZ\n",
156 DriverObject->HardwareDatabase));
157 return status;
158 }
159
160 status = DiskSaveGeometryDetectInfo(DriverObject, hardwareKey);
161
162 if(!NT_SUCCESS(status)) {
163 DebugPrint((1, "DiskSaveDetectInfo: Can't query configuration data "
164 "(%#08lx)\n",
165 status));
166 ZwClose(hardwareKey);
167 return status;
168 }
169
170 //
171 // Open EISA bus key.
172 //
173
174 RtlInitUnicodeString(&unicodeString, L"EisaAdapter");
175 InitializeObjectAttributes(&objectAttributes,
176 &unicodeString,
177 OBJ_CASE_INSENSITIVE,
178 hardwareKey,
179 NULL);
180
181 status = ZwOpenKey(&busKey,
182 KEY_READ,
183 &objectAttributes);
184
185 if(NT_SUCCESS(status)) {
186 DebugPrint((1, "DiskSaveDetectInfo: Opened EisaAdapter key\n"));
187 DiskScanBusDetectInfo(DriverObject, busKey);
188 ZwClose(busKey);
189 }
190
191 //
192 // Open MultiFunction bus key.
193 //
194
195 RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter");
196 InitializeObjectAttributes(&objectAttributes,
197 &unicodeString,
198 OBJ_CASE_INSENSITIVE,
199 hardwareKey,
200 NULL);
201
202 status = ZwOpenKey(&busKey,
203 KEY_READ,
204 &objectAttributes);
205
206 if(NT_SUCCESS(status)) {
207 DebugPrint((1, "DiskSaveDetectInfo: Opened MultifunctionAdapter key\n"));
208 DiskScanBusDetectInfo(DriverObject, busKey);
209 ZwClose(busKey);
210 }
211
212 ZwClose(hardwareKey);
213
214 return STATUS_SUCCESS;
215 }
216
217 VOID
218 NTAPI
219 DiskCleanupDetectInfo(
220 IN PDRIVER_OBJECT DriverObject
221 )
222 /*++
223
224 Routine Description:
225
226 This routine will cleanup the data structure built by DiskSaveDetectInfo.
227
228 Arguments:
229
230 DriverObject - a pointer to the kernel object for this driver.
231
232 Return Value:
233
234 none
235
236 --*/
237
238 {
239 if(DetectInfoList != NULL) {
240 ExFreePool(DetectInfoList);
241 DetectInfoList = NULL;
242 }
243 return;
244 }
245
246 NTSTATUS
247 NTAPI
248 DiskSaveGeometryDetectInfo(
249 IN PDRIVER_OBJECT DriverObject,
250 IN HANDLE HardwareKey
251 )
252 {
253 UNICODE_STRING unicodeString;
254 PKEY_VALUE_FULL_INFORMATION keyData;
255 ULONG length;
256
257 PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor;
258 PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
259
260 PCM_INT13_DRIVE_PARAMETER driveParameters;
261 ULONG numberOfDrives;
262
263 ULONG i;
264
265 NTSTATUS status;
266
267 PAGED_CODE();
268
269 //
270 // Get disk BIOS geometry information.
271 //
272
273 RtlInitUnicodeString(&unicodeString, L"Configuration Data");
274
275 keyData = ExAllocatePoolWithTag(PagedPool,
276 VALUE_BUFFER_SIZE,
277 DISK_TAG_UPDATE_GEOM);
278
279 if(keyData == NULL) {
280 DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't allocate config "
281 "data buffer\n"));
282 return STATUS_INSUFFICIENT_RESOURCES;
283 }
284
285 status = ZwQueryValueKey(HardwareKey,
286 &unicodeString,
287 KeyValueFullInformation,
288 keyData,
289 VALUE_BUFFER_SIZE,
290 &length);
291
292 if(!NT_SUCCESS(status)) {
293 DebugPrint((1, "DiskSaveGeometryDetectInfo: Can't query configuration "
294 "data (%#08lx)\n",
295 status));
296 ExFreePool(keyData);
297 return status;
298 }
299
300 //
301 // Extract the resource list out of the key data.
302 //
303
304 fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
305 (((PUCHAR) keyData) + keyData->DataOffset);
306 partialDescriptor =
307 fullDescriptor->PartialResourceList.PartialDescriptors;
308 length = partialDescriptor->u.DeviceSpecificData.DataSize;
309
310 if((keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) ||
311 (fullDescriptor->PartialResourceList.Count == 0) ||
312 (partialDescriptor->Type != CmResourceTypeDeviceSpecific) ||
313 (length < sizeof(ULONG))) {
314
315 DebugPrint((1, "DiskSaveGeometryDetectInfo: BIOS header data too small "
316 "or invalid\n"));
317 ExFreePool(keyData);
318 return STATUS_INVALID_PARAMETER;
319 }
320
321 //
322 // Point to the BIOS data. THe BIOS data is located after the first
323 // partial Resource list which should be device specific data.
324 //
325
326 {
327 PUCHAR buffer = (PUCHAR) keyData;
328 buffer += keyData->DataOffset;
329 buffer += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
330 driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer;
331 }
332
333 numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER);
334
335 //
336 // Allocate our detect info list now that we know how many entries there
337 // are going to be. No other routine allocates detect info and this is
338 // done out of DriverEntry so we don't need to synchronize it's creation.
339 //
340
341 length = sizeof(DISK_DETECT_INFO) * numberOfDrives;
342 DetectInfoList = ExAllocatePoolWithTag(PagedPool,
343 length,
344 DISK_TAG_UPDATE_GEOM);
345
346 if(DetectInfoList == NULL) {
347 DebugPrint((1, "DiskSaveGeometryDetectInfo: Couldn't allocate %x bytes "
348 "for DetectInfoList\n",
349 length));
350
351 ExFreePool(keyData);
352 return STATUS_INSUFFICIENT_RESOURCES;
353 }
354
355 DetectInfoCount = numberOfDrives;
356
357 RtlZeroMemory(DetectInfoList, length);
358
359 //
360 // Copy the information out of the key data and into the list we've
361 // allocated.
362 //
363
364 for(i = 0; i < numberOfDrives; i++) {
365 DetectInfoList[i].DriveParameters = driveParameters[i];
366 }
367
368 ExFreePool(keyData);
369 return STATUS_SUCCESS;
370 }
371
372 VOID
373 NTAPI
374 DiskScanBusDetectInfo(
375 IN PDRIVER_OBJECT DriverObject,
376 IN HANDLE BusKey
377 )
378 /*++
379
380 Routine Description:
381
382 The routine queries the registry to determine which disks are visible to
383 the BIOS. If a disk is visable to the BIOS then the geometry information
384 is updated with the disk's signature and MBR checksum.
385
386 Arguments:
387
388 DriverObject - the object for this driver.
389 BusKey - handle to the bus key to be enumerated.
390
391 Return Value:
392
393 status
394
395 --*/
396 {
397 ULONG busNumber;
398
399 NTSTATUS status;
400
401 for(busNumber = 0; ; busNumber++) {
402
403 WCHAR buffer[32];
404 UNICODE_STRING unicodeString;
405
406 OBJECT_ATTRIBUTES objectAttributes;
407
408 HANDLE spareKey;
409 HANDLE adapterKey;
410
411 ULONG adapterNumber;
412
413 DebugPrint((1, "DiskScanBusDetectInfo: Scanning bus %d\n", busNumber));
414
415 //
416 // Open controller name key.
417 //
418
419 swprintf(buffer, L"%d", busNumber);
420 RtlInitUnicodeString(&unicodeString, buffer);
421
422 InitializeObjectAttributes(&objectAttributes,
423 &unicodeString,
424 OBJ_CASE_INSENSITIVE,
425 BusKey,
426 NULL);
427
428 status = ZwOpenKey(&spareKey, KEY_READ, &objectAttributes);
429
430 if(!NT_SUCCESS(status)) {
431 DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening bus "
432 "key %#x\n",
433 status, busNumber));
434 break;
435 }
436
437 //
438 // Open up a controller ordinal key.
439 //
440
441 RtlInitUnicodeString(&unicodeString, L"DiskController");
442 InitializeObjectAttributes(&objectAttributes,
443 &unicodeString,
444 OBJ_CASE_INSENSITIVE,
445 spareKey,
446 NULL);
447
448 status = ZwOpenKey(&adapterKey, KEY_READ, &objectAttributes);
449 ZwClose(spareKey);
450
451 if(!NT_SUCCESS(status)) {
452 DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening "
453 "DiskController key\n",
454 status));
455 continue;
456 }
457
458 for(adapterNumber = 0; ; adapterNumber++) {
459
460 HANDLE diskKey;
461 ULONG diskNumber;
462
463 //
464 // Open disk key.
465 //
466
467 DebugPrint((1, "DiskScanBusDetectInfo: Scanning disk key "
468 "%d\\DiskController\\%d\\DiskPeripheral\n",
469 busNumber, adapterNumber));
470
471 swprintf(buffer, L"%d\\DiskPeripheral", adapterNumber);
472 RtlInitUnicodeString(&unicodeString, buffer);
473
474 InitializeObjectAttributes(&objectAttributes,
475 &unicodeString,
476 OBJ_CASE_INSENSITIVE,
477 adapterKey,
478 NULL);
479
480 status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
481
482 if(!NT_SUCCESS(status)) {
483 DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx opening "
484 "disk key\n",
485 status));
486 break;
487 }
488
489 for(diskNumber = 0; ; diskNumber++) {
490
491 HANDLE targetKey;
492
493 DebugPrint((1, "DiskScanBusDetectInfo: Scanning target key "
494 "%d\\DiskController\\%d\\DiskPeripheral\\%d\n",
495 busNumber, adapterNumber, diskNumber));
496
497 swprintf(buffer, L"%d", diskNumber);
498 RtlInitUnicodeString(&unicodeString, buffer);
499
500 InitializeObjectAttributes(&objectAttributes,
501 &unicodeString,
502 OBJ_CASE_INSENSITIVE,
503 diskKey,
504 NULL);
505
506 status = ZwOpenKey(&targetKey, KEY_READ, &objectAttributes);
507
508 if(!NT_SUCCESS(status)) {
509 DebugPrint((1, "DiskScanBusDetectInfo: Error %#08lx "
510 "opening target key\n",
511 status));
512 break;
513 }
514
515 status = DiskSaveBusDetectInfo(DriverObject,
516 targetKey,
517 diskNumber);
518
519 ZwClose(targetKey);
520 }
521
522 ZwClose(diskKey);
523 }
524 ZwClose(adapterKey);
525 }
526 return;
527 }
528
529 NTSTATUS
530 NTAPI
531 DiskSaveBusDetectInfo(
532 IN PDRIVER_OBJECT DriverObject,
533 IN HANDLE TargetKey,
534 IN ULONG DiskNumber
535 )
536 /*++
537
538 Routine Description:
539
540 This routine will transfer the firmware/ntdetect reported information
541 in the specified target key into the appropriate entry in the
542 DetectInfoList.
543
544 Arguments:
545
546 DriverObject - the object for this driver.
547
548 TargetKey - the key for the disk being saved.
549
550 DiskNumber - the ordinal of the entry in the DiskPeripheral tree for this
551 entry
552
553 Return Value:
554
555 status
556
557 --*/
558 {
559 PDISK_DETECT_INFO diskInfo;
560
561 UNICODE_STRING unicodeString;
562
563 PKEY_VALUE_FULL_INFORMATION keyData;
564 ULONG length;
565
566 NTSTATUS status;
567
568 PAGED_CODE();
569
570 diskInfo = &(DetectInfoList[DiskNumber]);
571
572 if(diskInfo->Initialized) {
573
574 ASSERT(FALSE);
575 DebugPrint((1, "DiskSaveBusDetectInfo: disk entry %#x already has a "
576 "signature of %#08lx and mbr checksum of %#08lx\n",
577 DiskNumber,
578 diskInfo->Signature,
579 diskInfo->MbrCheckSum));
580 return STATUS_UNSUCCESSFUL;
581 }
582
583 RtlInitUnicodeString(&unicodeString, L"Identifier");
584
585 keyData = ExAllocatePoolWithTag(PagedPool,
586 VALUE_BUFFER_SIZE,
587 DISK_TAG_UPDATE_GEOM);
588
589 if(keyData == NULL) {
590 DebugPrint((1, "DiskSaveBusDetectInfo: Couldn't allocate space for "
591 "registry data\n"));
592 return STATUS_INSUFFICIENT_RESOURCES;
593 }
594
595 //
596 // Get disk peripheral identifier.
597 //
598
599 status = ZwQueryValueKey(TargetKey,
600 &unicodeString,
601 KeyValueFullInformation,
602 keyData,
603 VALUE_BUFFER_SIZE,
604 &length);
605
606 if(!NT_SUCCESS(status)) {
607 DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx getting "
608 "Identifier\n",
609 status));
610 ExFreePool(keyData);
611 return status;
612
613 } else if (keyData->DataLength < 9*sizeof(WCHAR)) {
614
615 //
616 // the data is too short to use (we subtract 9 chars in normal path)
617 //
618 DebugPrint((1, "DiskSaveBusDetectInfo: Saved data was invalid, "
619 "not enough data in registry!\n"));
620 ExFreePool(keyData);
621 return STATUS_UNSUCCESSFUL;
622
623 } else {
624
625 UNICODE_STRING identifier;
626 ULONG value;
627
628 //
629 // Complete unicode string.
630 //
631
632 identifier.Buffer = (PWSTR) ((PUCHAR)keyData + keyData->DataOffset);
633 identifier.Length = (USHORT) keyData->DataLength;
634 identifier.MaximumLength = (USHORT) keyData->DataLength;
635
636 //
637 // Get the first value out of the identifier - this will be the MBR
638 // checksum.
639 //
640
641 status = RtlUnicodeStringToInteger(&identifier, 16, &value);
642
643 if(!NT_SUCCESS(status)) {
644 DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting "
645 "identifier %wZ into MBR xsum\n",
646 status,
647 &identifier));
648 ExFreePool(keyData);
649 return status;
650 }
651
652 diskInfo->MbrCheckSum = value;
653
654 //
655 // Shift the string over to get the disk signature
656 //
657
658 identifier.Buffer += 9;
659 identifier.Length -= 9 * sizeof(WCHAR);
660 identifier.MaximumLength -= 9 * sizeof(WCHAR);
661
662 status = RtlUnicodeStringToInteger(&identifier, 16, &value);
663
664 if(!NT_SUCCESS(status)) {
665 DebugPrint((1, "DiskSaveBusDetectInfo: Error %#08lx converting "
666 "identifier %wZ into disk signature\n",
667 status,
668 &identifier));
669 ExFreePool(keyData);
670 value = 0;
671 }
672
673 diskInfo->Signature = value;
674 }
675
676 //
677 // Here is where we would save away the extended int13 data.
678 //
679
680 //
681 // Mark this entry as initialized so we can make sure not to do it again.
682 //
683
684 diskInfo->Initialized = TRUE;
685
686
687 return STATUS_SUCCESS;
688 }
689
690 DISK_GEOMETRY_SOURCE
691 NTAPI
692 DiskUpdateGeometry(
693 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
694 )
695 /*++
696
697 Routine Description:
698
699 This routine checks the DetectInfoList saved away during disk driver init
700 to see if any geometry information was reported for this drive. If the
701 geometry data exists (determined by matching non-zero signatures or
702 non-zero MBR checksums) then it will be saved in the RealGeometry member
703 of the disk data block.
704
705 ClassReadDriveCapacity MUST be called after calling this routine to update
706 the cylinder count based on the size of the disk and the presence of any
707 disk management software.
708
709 Arguments:
710
711 DeviceExtension - Supplies a pointer to the device information for disk.
712
713 Return Value:
714
715 Inidicates whether the "RealGeometry" in the data block is now valid.
716
717 --*/
718
719 {
720 PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
721
722 ULONG i;
723 PDISK_DETECT_INFO diskInfo;
724
725 BOOLEAN found = FALSE;
726
727 NTSTATUS status;
728
729 PAGED_CODE();
730
731
732 ASSERT(FdoExtension->CommonExtension.IsFdo);
733 ASSERT((FdoExtension->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0);
734
735 //
736 // If we've already set a non-default geometry for this drive then there's
737 // no need to try and update again.
738 //
739
740 if(diskData->GeometrySource != DiskGeometryUnknown) {
741 return diskData->GeometrySource;
742 }
743
744 //
745 // Scan through the saved detect info to see if we can find a match
746 // for this device.
747 //
748
749 for(i = 0; i < DetectInfoCount; i++) {
750
751 ASSERT(DetectInfoList != NULL);
752
753 diskInfo = &(DetectInfoList[i]);
754
755 if((diskData->Mbr.Signature != 0) &&
756 (diskData->Mbr.Signature == diskInfo->Signature)) {
757 DebugPrint((1, "DiskUpdateGeometry: found match for signature "
758 "%#08lx\n",
759 diskData->Mbr.Signature));
760 found = TRUE;
761 break;
762 } else if((diskData->Mbr.Signature == 0) &&
763 (diskData->Mbr.MbrCheckSum != 0) &&
764 (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) {
765 DebugPrint((1, "DiskUpdateGeometry: found match for xsum %#08lx\n",
766 diskData->Mbr.MbrCheckSum));
767 found = TRUE;
768 break;
769 }
770 }
771
772 if(found) {
773
774 ULONG cylinders;
775 ULONG sectorsPerTrack;
776 ULONG tracksPerCylinder;
777
778 //ULONG sectors;
779 ULONG length;
780
781 //
782 // Point to the array of drive parameters.
783 //
784
785 cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
786 sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
787 tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
788
789 //
790 // Since the BIOS may not report the full drive, recalculate the drive
791 // size based on the volume size and the BIOS values for tracks per
792 // cylinder and sectors per track..
793 //
794
795 length = tracksPerCylinder * sectorsPerTrack;
796
797 if (length == 0) {
798
799 //
800 // The BIOS information is bogus.
801 //
802
803 DebugPrint((1, "DiskUpdateGeometry: H (%d) or S(%d) is zero\n",
804 tracksPerCylinder, sectorsPerTrack));
805 return FALSE;
806 }
807
808 //
809 // since we are copying the structure RealGeometry here, we should
810 // really initialize all the fields, especially since a zero'd
811 // BytesPerSector field would cause a trap in xHalReadPartitionTable()
812 //
813
814 diskData->RealGeometry = FdoExtension->DiskGeometry;
815
816 //
817 // Save the geometry information away in the disk data block and
818 // set the bit indicating that we found a valid one.
819 //
820
821 diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
822 diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
823 diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
824
825 DebugPrint((1, "DiskUpdateGeometry: BIOS spt %#x, #heads %#x, "
826 "#cylinders %#x\n",
827 sectorsPerTrack, tracksPerCylinder, cylinders));
828
829 diskData->GeometrySource = DiskGeometryFromBios;
830 diskInfo->Device = FdoExtension->DeviceObject;
831
832 } else {
833
834 DebugPrint((1, "DiskUpdateGeometry: no match found for signature %#08lx\n", diskData->Mbr.Signature));
835 }
836
837 if(diskData->GeometrySource == DiskGeometryUnknown) {
838
839 //
840 // We couldn't find a geometry from the BIOS. Check with the port
841 // driver and see if it can provide one.
842 //
843
844 status = DiskGetPortGeometry(FdoExtension, &(diskData->RealGeometry));
845
846 if(NT_SUCCESS(status)) {
847
848 //
849 // Check the geometry to make sure it's valid.
850 //
851
852 if((diskData->RealGeometry.TracksPerCylinder *
853 diskData->RealGeometry.SectorsPerTrack) != 0) {
854
855 diskData->GeometrySource = DiskGeometryFromPort;
856 DebugPrint((1, "DiskUpdateGeometry: using Port geometry for disk %#p\n", FdoExtension));
857
858 if (diskData->RealGeometry.BytesPerSector == 0) {
859
860 DebugPrint((0, "DiskDriverReinit: Port driver failed to "
861 "set BytesPerSector in the RealGeometry\n"));
862 diskData->RealGeometry.BytesPerSector =
863 FdoExtension->DiskGeometry.BytesPerSector;
864 if (diskData->RealGeometry.BytesPerSector == 0) {
865 ASSERT(!"BytesPerSector is still zero!");
866 }
867
868 }
869 }
870 }
871 }
872
873 //
874 // If we came up with a "real" geometry for this drive then set it in the
875 // device extension.
876 //
877
878 if(diskData->GeometrySource != DiskGeometryUnknown) {
879 FdoExtension->DiskGeometry = diskData->RealGeometry;
880
881 //
882 // Increment the count of used geometry entries.
883 //
884
885 InterlockedIncrement(&DetectInfoUsedCount);
886 }
887
888 return diskData->GeometrySource;
889 }
890
891 NTSTATUS
892 NTAPI
893 DiskUpdateRemovableGeometry (
894 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
895 )
896
897 /*++
898
899 Routine Description:
900
901 This routine updates the geometry of the disk. It will query the port
902 driver to see if it can provide any geometry info. If not it will use
903 the current head & sector count.
904
905 Based on these values & the capacity of the drive as reported by
906 ClassReadDriveCapacity it will determine a new cylinder count for the
907 device.
908
909 Arguments:
910
911 Fdo - Supplies the functional device object whos size needs to be updated.
912
913 Return Value:
914
915 Returns the status of the opertion.
916
917 --*/
918 {
919 PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
920 PDISK_DATA diskData = commonExtension->DriverData;
921 PDISK_GEOMETRY geometry = &(diskData->RealGeometry);
922
923 NTSTATUS status;
924
925 PAGED_CODE();
926
927 ASSERT_FDO(commonExtension->DeviceObject);
928 if (FdoExtension->DeviceDescriptor) {
929 ASSERT(FdoExtension->DeviceDescriptor->RemovableMedia);
930 }
931 ASSERT(TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
932 FILE_REMOVABLE_MEDIA));
933
934 //
935 // Attempt to determine the disk geometry. First we'll check with the
936 // port driver to see what it suggests for a value.
937 //
938
939 status = DiskGetPortGeometry(FdoExtension, geometry);
940
941 if(NT_SUCCESS(status) &&
942 ((geometry->TracksPerCylinder * geometry->SectorsPerTrack) != 0)) {
943
944 FdoExtension->DiskGeometry = (*geometry);
945 }
946
947 return status;
948 }
949
950 NTSTATUS
951 NTAPI
952 DiskGetPortGeometry(
953 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
954 OUT PDISK_GEOMETRY Geometry
955 )
956 /*++
957
958 Routine Description:
959
960 This routine will query the port driver for disk geometry. Some port
961 drivers (in particular IDEPORT) may be able to provide geometry for the
962 device.
963
964 Arguments:
965
966 FdoExtension - the device object for the disk.
967
968 Geometry - a structure to save the geometry information into (if any is
969 available)
970
971 Return Value:
972
973 STATUS_SUCCESS if geometry information can be provided or
974 error status indicating why it can't.
975
976 --*/
977 {
978 PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
979 PIRP irp;
980 PIO_STACK_LOCATION irpStack;
981 KEVENT event;
982
983 NTSTATUS status;
984
985 PAGED_CODE();
986
987 //
988 // Build an irp to send IOCTL_DISK_GET_DRIVE_GEOMETRY to the lower driver.
989 //
990
991 irp = IoAllocateIrp(commonExtension->LowerDeviceObject->StackSize, FALSE);
992
993 if(irp == NULL) {
994 return STATUS_INSUFFICIENT_RESOURCES;
995 }
996
997 irpStack = IoGetNextIrpStackLocation(irp);
998
999 irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
1000
1001 irpStack->Parameters.DeviceIoControl.IoControlCode =
1002 IOCTL_DISK_GET_DRIVE_GEOMETRY;
1003 irpStack->Parameters.DeviceIoControl.OutputBufferLength =
1004 sizeof(DISK_GEOMETRY);
1005
1006 irp->AssociatedIrp.SystemBuffer = Geometry;
1007
1008 KeInitializeEvent(&event, SynchronizationEvent, FALSE);
1009
1010 IoSetCompletionRoutine(irp,
1011 (PIO_COMPLETION_ROUTINE)ClassSignalCompletion,
1012 &event,
1013 TRUE,
1014 TRUE,
1015 TRUE);
1016
1017 status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
1018 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
1019
1020 ASSERT((status == STATUS_PENDING) || (status == irp->IoStatus.Status));
1021 status = irp->IoStatus.Status;
1022
1023 IoFreeIrp(irp);
1024
1025 return status;
1026 }
1027
1028 NTSTATUS
1029 NTAPI
1030 DiskReadDriveCapacity(
1031 IN PDEVICE_OBJECT Fdo
1032 )
1033 /*++
1034
1035 Routine Description:
1036
1037 This routine is used by disk.sys as a wrapper for the classpnp API
1038 ClassReadDriveCapacity. It will perform some additional operations to
1039 attempt to determine drive geometry before it calls the classpnp version
1040 of the routine.
1041
1042 For fixed disks this involves calling DiskUpdateGeometry which will check
1043 various sources (the BIOS, the port driver) for geometry information.
1044
1045 Arguments:
1046
1047 Fdo - a pointer to the device object to be checked.
1048
1049 Return Value:
1050
1051 status of ClassReadDriveCapacity.
1052
1053 --*/
1054
1055 {
1056 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
1057 //PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
1058 //DISK_GEOMETRY_SOURCE diskGeometrySource = DiskGeometryUnknown;
1059 NTSTATUS status;
1060
1061 ASSERT_FDO(Fdo);
1062
1063 if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
1064 DiskUpdateRemovableGeometry(fdoExtension);
1065 } else {
1066 /* diskGeometrySource =*/ DiskUpdateGeometry(fdoExtension);
1067 }
1068
1069 status = ClassReadDriveCapacity(Fdo);
1070
1071 return status;
1072 }
1073
1074 VOID
1075 NTAPI
1076 DiskDriverReinitialization(
1077 IN PDRIVER_OBJECT DriverObject,
1078 IN PVOID Nothing,
1079 IN ULONG Count
1080 )
1081 /*++
1082
1083 Routine Description:
1084
1085 This routine will scan through the current list of disks and attempt to
1086 match them to any remaining geometry information. This will only be done
1087 on the first call to the routine.
1088
1089 Note: This routine assumes that the system will not be adding or removing
1090 devices during this phase of the init process. This is very likely
1091 a bad assumption but it greatly simplifies the code.
1092
1093 Arguments:
1094
1095 DriverObject - a pointer to the object for the disk driver.
1096
1097 Nothing - unused
1098
1099 Count - an indication of how many times this routine has been called.
1100
1101 Return Value:
1102
1103 none
1104
1105 --*/
1106
1107 {
1108 PDEVICE_OBJECT deviceObject;
1109 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1110 PDISK_DATA diskData;
1111
1112 ULONG unmatchedDiskCount;
1113 PDEVICE_OBJECT unmatchedDisk = NULL;
1114
1115 ULONG i;
1116 PDISK_DETECT_INFO diskInfo = NULL;
1117
1118 if(Count != 1) {
1119 DebugPrint((1, "DiskDriverReinitialization: ignoring call %d\n",
1120 Count));
1121 return;
1122 }
1123
1124 //
1125 // Check to see how many entries in the detect info list have been matched.
1126 // If there's only one remaining we'll see if we can find a disk to go with
1127 // it.
1128 //
1129
1130 if(DetectInfoCount == 0) {
1131 DebugPrint((1, "DiskDriverReinitialization: no detect info saved\n"));
1132 return;
1133 }
1134
1135 if((DetectInfoCount - DetectInfoUsedCount) != 1) {
1136 DebugPrint((1, "DiskDriverReinitialization: %d of %d geometry entries "
1137 "used - will not attempt match\n"));
1138 return;
1139 }
1140
1141 //
1142 // Scan through the list of disks and see if any of them are missing
1143 // geometry information. If there is only one such disk we'll try to
1144 // match it to the unmatched geometry.
1145 //
1146
1147
1148 //
1149 // ISSUE-2000/5/24-henrygab - figure out if there's a way to keep
1150 // removals from happening while doing this.
1151 //
1152
1153 for(deviceObject = DriverObject->DeviceObject, unmatchedDiskCount = 0;
1154 deviceObject != NULL;
1155 deviceObject = deviceObject->NextDevice) {
1156
1157 //
1158 // Make sure this is a disk and not a partition.
1159 //
1160
1161 fdoExtension = deviceObject->DeviceExtension;
1162 if(fdoExtension->CommonExtension.IsFdo == FALSE) {
1163 DebugPrint((1, "DiskDriverReinit: DO %#p is not an FDO\n",
1164 deviceObject));
1165 continue;
1166 }
1167
1168 //
1169 // If the geometry for this one is already known then skip it.
1170 //
1171
1172 diskData = fdoExtension->CommonExtension.DriverData;
1173 if(diskData->GeometrySource != DiskGeometryUnknown) {
1174 DebugPrint((1, "DiskDriverReinit: FDO %#p has a geometry\n",
1175 deviceObject));
1176 continue;
1177 }
1178
1179 DebugPrint((1, "DiskDriverReinit: FDO %#p has no geometry\n",
1180 deviceObject));
1181
1182 //
1183 // Mark this one as using the default. It's past the time when disk
1184 // might blunder across the geometry info. If we set the geometry
1185 // from the bios we'll reset this field down below.
1186 //
1187
1188 diskData->GeometrySource = DiskGeometryFromDefault;
1189
1190 //
1191 // As long as we've only got one unmatched disk we're fine.
1192 //
1193
1194 unmatchedDiskCount++;
1195 if(unmatchedDiskCount > 1) {
1196 ASSERT(unmatchedDisk != NULL);
1197 DebugPrint((1, "DiskDriverReinit: FDO %#p also has no geometry\n",
1198 unmatchedDisk));
1199 unmatchedDisk = NULL;
1200 break;
1201 }
1202
1203 unmatchedDisk = deviceObject;
1204 }
1205
1206 //
1207 // If there's more or less than one ungeometried disk then we can't do
1208 // anything about the geometry.
1209 //
1210
1211 if(unmatchedDiskCount != 1) {
1212 DebugPrint((1, "DiskDriverReinit: Unable to match geometry\n"));
1213 return;
1214
1215 }
1216
1217 fdoExtension = unmatchedDisk->DeviceExtension;
1218 diskData = fdoExtension->CommonExtension.DriverData;
1219
1220 DebugPrint((1, "DiskDriverReinit: Found possible match\n"));
1221
1222 //
1223 // Find the geometry which wasn't assigned.
1224 //
1225
1226 for(i = 0; i < DetectInfoCount; i++) {
1227 if(DetectInfoList[i].Device == NULL) {
1228 diskInfo = &(DetectInfoList[i]);
1229 break;
1230 }
1231 }
1232
1233 ASSERT(diskInfo != NULL);
1234
1235 {
1236 //
1237 // Save the geometry information away in the disk data block and
1238 // set the bit indicating that we found a valid one.
1239 //
1240
1241 ULONG cylinders;
1242 ULONG sectorsPerTrack;
1243 ULONG tracksPerCylinder;
1244
1245 //ULONG sectors;
1246 ULONG length;
1247
1248 //
1249 // Point to the array of drive parameters.
1250 //
1251
1252 cylinders = diskInfo->DriveParameters.MaxCylinders + 1;
1253 sectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
1254 tracksPerCylinder = diskInfo->DriveParameters.MaxHeads + 1;
1255
1256 //
1257 // Since the BIOS may not report the full drive, recalculate the drive
1258 // size based on the volume size and the BIOS values for tracks per
1259 // cylinder and sectors per track..
1260 //
1261
1262 length = tracksPerCylinder * sectorsPerTrack;
1263
1264 if (length == 0) {
1265
1266 //
1267 // The BIOS information is bogus.
1268 //
1269
1270 DebugPrint((1, "DiskDriverReinit: H (%d) or S(%d) is zero\n",
1271 tracksPerCylinder, sectorsPerTrack));
1272 return;
1273 }
1274
1275 //
1276 // since we are copying the structure RealGeometry here, we should
1277 // really initialize all the fields, especially since a zero'd
1278 // BytesPerSector field would cause a trap in xHalReadPartitionTable()
1279 //
1280
1281 diskData->RealGeometry = fdoExtension->DiskGeometry;
1282
1283 //
1284 // Save the geometry information away in the disk data block and
1285 // set the bit indicating that we found a valid one.
1286 //
1287
1288 diskData->RealGeometry.SectorsPerTrack = sectorsPerTrack;
1289 diskData->RealGeometry.TracksPerCylinder = tracksPerCylinder;
1290 diskData->RealGeometry.Cylinders.QuadPart = (LONGLONG)cylinders;
1291
1292 DebugPrint((1, "DiskDriverReinit: BIOS spt %#x, #heads %#x, "
1293 "#cylinders %#x\n",
1294 sectorsPerTrack, tracksPerCylinder, cylinders));
1295
1296 diskData->GeometrySource = DiskGeometryGuessedFromBios;
1297 diskInfo->Device = unmatchedDisk;
1298
1299 //
1300 // Now copy the geometry over to the fdo extension and call
1301 // classpnp to redetermine the disk size and cylinder count.
1302 //
1303
1304 fdoExtension->DiskGeometry = diskData->RealGeometry;
1305
1306 //
1307 // BUGBUG - why not call DiskReadDriveCapacity()?
1308 //
1309
1310 ClassReadDriveCapacity(unmatchedDisk);
1311
1312 if (diskData->RealGeometry.BytesPerSector == 0) {
1313
1314 //
1315 // if the BytesPerSector field is set to zero for a disk
1316 // listed in the bios, then the system will bugcheck in
1317 // xHalReadPartitionTable(). assert here since it is
1318 // easier to determine what is happening this way.
1319 //
1320
1321 ASSERT(!"RealGeometry not set to non-zero bps\n");
1322 }
1323 }
1324
1325 return;
1326 }
1327
1328 NTSTATUS
1329 NTAPI
1330 DiskGetDetectInfo(
1331 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1332 OUT PDISK_DETECTION_INFO DetectInfo
1333 )
1334 /*++
1335
1336 Routine Description:
1337
1338 Get the Int13 information from the BIOS DetectInfoList.
1339
1340 Arguments:
1341
1342 FdoExtension - Supplies a pointer to the FDO extension that we want to
1343 obtain the detect information for.
1344
1345 DetectInfo - A buffer where the detect information will be copied to.
1346
1347 Return Value:
1348
1349 NTSTATUS code.
1350
1351 --*/
1352 {
1353 ULONG i;
1354 BOOLEAN found;
1355 PDISK_DETECT_INFO diskInfo = NULL;
1356 PDISK_DATA diskData = FdoExtension->CommonExtension.DriverData;
1357
1358 PAGED_CODE ();
1359
1360 ASSERT(FdoExtension->CommonExtension.IsFdo);
1361
1362 //
1363 // Fail for non-fixed drives.
1364 //
1365
1366 if (TEST_FLAG (FdoExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
1367 return STATUS_NOT_SUPPORTED;
1368 }
1369
1370 //
1371 // There is no GPT detection info, so fail this.
1372 //
1373
1374 if (diskData->PartitionStyle == PARTITION_STYLE_GPT) {
1375 return STATUS_NOT_SUPPORTED;
1376 }
1377
1378 for(i = 0; i < DetectInfoCount; i++) {
1379
1380
1381 ASSERT(DetectInfoList != NULL);
1382
1383 diskInfo = &(DetectInfoList[i]);
1384
1385 if((diskData->Mbr.Signature != 0) &&
1386 (diskData->Mbr.Signature == diskInfo->Signature)) {
1387 DebugPrint((1, "DiskGetDetectInfo: found match for signature "
1388 "%#08lx\n",
1389 diskData->Mbr.Signature));
1390 found = TRUE;
1391 break;
1392 } else if((diskData->Mbr.Signature == 0) &&
1393 (diskData->Mbr.MbrCheckSum != 0) &&
1394 (diskData->Mbr.MbrCheckSum == diskInfo->MbrCheckSum)) {
1395 DebugPrint((1, "DiskGetDetectInfo: found match for xsum %#08lx\n",
1396 diskData->Mbr.MbrCheckSum));
1397 found = TRUE;
1398 break;
1399 }
1400 }
1401
1402 if ( found ) {
1403 DetectInfo->DetectionType = DetectInt13;
1404 DetectInfo->Int13.DriveSelect = diskInfo->DriveParameters.DriveSelect;
1405 DetectInfo->Int13.MaxCylinders = diskInfo->DriveParameters.MaxCylinders;
1406 DetectInfo->Int13.SectorsPerTrack = diskInfo->DriveParameters.SectorsPerTrack;
1407 DetectInfo->Int13.MaxHeads = diskInfo->DriveParameters.MaxHeads;
1408 DetectInfo->Int13.NumberDrives = diskInfo->DriveParameters.NumberDrives;
1409 RtlZeroMemory (&DetectInfo->ExInt13, sizeof (DetectInfo->ExInt13));
1410 }
1411
1412 return (found ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
1413 }
1414
1415 NTSTATUS
1416 NTAPI
1417 DiskReadSignature(
1418 IN PDEVICE_OBJECT Fdo
1419 )
1420
1421 /*++
1422
1423 Routine Description:
1424
1425 Read the disks signature from the drive. The signature can be either
1426 a MBR signature or a GPT/EFI signature.
1427
1428 The low-level signature reading is done by IoReadDiskSignature().
1429
1430 Arguments:
1431
1432 Fdo - Pointer to the FDO of a disk to read the signature for.
1433
1434 Return Value:
1435
1436 NTSTATUS code.
1437
1438 --*/
1439
1440
1441 {
1442 NTSTATUS Status;
1443 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
1444 PDISK_DATA diskData = fdoExtension->CommonExtension.DriverData;
1445 DISK_SIGNATURE Signature;
1446
1447 PAGED_CODE ();
1448
1449 Status = IoReadDiskSignature (Fdo,
1450 fdoExtension->DiskGeometry.BytesPerSector,
1451 &Signature);
1452
1453 if (!NT_SUCCESS (Status)) {
1454 return Status;
1455 }
1456
1457 if (Signature.PartitionStyle == PARTITION_STYLE_GPT) {
1458 diskData->PartitionStyle = PARTITION_STYLE_GPT;
1459 diskData->Efi.DiskId = Signature.Gpt.DiskId;
1460 } else if (Signature.PartitionStyle == PARTITION_STYLE_MBR) {
1461 diskData->PartitionStyle = PARTITION_STYLE_MBR;
1462 diskData->Mbr.Signature = Signature.Mbr.Signature;
1463 diskData->Mbr.MbrCheckSum = Signature.Mbr.CheckSum;
1464 } else {
1465 ASSERT (FALSE);
1466 Status = STATUS_UNSUCCESSFUL;
1467 }
1468
1469 return Status;
1470 }
1471
1472 #endif // defined(_X86_)