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