4 * Copyright (C) 2003, 2004 Eric Kohl
5 * Copyright (C) 2009 Hervé Poussineau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 DBG_DEFAULT_CHANNEL(HWDETECT
);
28 * This is the common code for harddisk for both the PC and the XBOX.
31 typedef struct tagDISKCONTEXT
35 ULONGLONG SectorOffset
;
36 ULONGLONG SectorCount
;
37 ULONGLONG SectorNumber
;
40 static const CHAR Hex
[] = "0123456789abcdef";
42 /* Data cache for BIOS disks pre-enumeration */
43 UCHAR PcBiosDiskCount
= 0;
44 static CHAR PcDiskIdentifier
[32][20];
47 SIZE_T DiskReadBufferSize
;
50 /* FUNCTIONS *****************************************************************/
53 DiskClose(ULONG FileId
)
55 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
56 FrLdrTempFree(Context
, TAG_HW_DISK_CONTEXT
);
61 DiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
63 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
65 RtlZeroMemory(Information
, sizeof(*Information
));
68 * The ARC specification mentions that for partitions, StartingAddress and
69 * EndingAddress are the start and end positions of the partition in terms
70 * of byte offsets from the start of the disk.
71 * CurrentAddress is the current offset into (i.e. relative to) the partition.
73 Information
->StartingAddress
.QuadPart
= Context
->SectorOffset
* Context
->SectorSize
;
74 Information
->EndingAddress
.QuadPart
= (Context
->SectorOffset
+ Context
->SectorCount
) * Context
->SectorSize
;
75 Information
->CurrentAddress
.QuadPart
= Context
->SectorNumber
* Context
->SectorSize
;
81 DiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
85 ULONG DrivePartition
, SectorSize
;
86 ULONGLONG SectorOffset
= 0;
87 ULONGLONG SectorCount
= 0;
88 PARTITION_TABLE_ENTRY PartitionTableEntry
;
90 if (DiskReadBufferSize
== 0)
92 ERR("DiskOpen(): DiskReadBufferSize is 0, something is wrong.\n");
97 if (!DissectArcPath(Path
, NULL
, &DriveNumber
, &DrivePartition
))
100 if (DrivePartition
== 0xff)
102 /* This is a CD-ROM device */
108 * This is either a floppy disk device (DrivePartition == 0) or
109 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF)
110 * but it doesn't matter which one because they both have 512 bytes
116 if (DrivePartition
!= 0xff && DrivePartition
!= 0)
118 if (!DiskGetPartitionEntry(DriveNumber
, DrivePartition
, &PartitionTableEntry
))
121 SectorOffset
= PartitionTableEntry
.SectorCountBeforePartition
;
122 SectorCount
= PartitionTableEntry
.PartitionSectorCount
;
127 if (!MachDiskGetDriveGeometry(DriveNumber
, &Geometry
))
130 if (SectorSize
!= Geometry
.BytesPerSector
)
132 ERR("SectorSize (%lu) != Geometry.BytesPerSector (%lu), expect problems!\n",
133 SectorSize
, Geometry
.BytesPerSector
);
137 SectorCount
= (ULONGLONG
)Geometry
.Cylinders
* Geometry
.Heads
* Geometry
.Sectors
;
140 Context
= FrLdrTempAlloc(sizeof(DISKCONTEXT
), TAG_HW_DISK_CONTEXT
);
144 Context
->DriveNumber
= DriveNumber
;
145 Context
->SectorSize
= SectorSize
;
146 Context
->SectorOffset
= SectorOffset
;
147 Context
->SectorCount
= SectorCount
;
148 Context
->SectorNumber
= 0;
149 FsSetDeviceSpecific(*FileId
, Context
);
155 DiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
157 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
158 UCHAR
* Ptr
= (UCHAR
*)Buffer
;
159 ULONG Length
, TotalSectors
, MaxSectors
, ReadSectors
;
160 ULONGLONG SectorOffset
;
163 ASSERT(DiskReadBufferSize
> 0);
165 TotalSectors
= (N
+ Context
->SectorSize
- 1) / Context
->SectorSize
;
166 MaxSectors
= DiskReadBufferSize
/ Context
->SectorSize
;
167 SectorOffset
= Context
->SectorOffset
+ Context
->SectorNumber
;
169 // If MaxSectors is 0, this will lead to infinite loop.
170 // In release builds assertions are disabled, however we also have sanity checks in DiskOpen()
171 ASSERT(MaxSectors
> 0);
177 ReadSectors
= TotalSectors
;
178 if (ReadSectors
> MaxSectors
)
179 ReadSectors
= MaxSectors
;
181 ret
= MachDiskReadLogicalSectors(Context
->DriveNumber
,
188 Length
= ReadSectors
* Context
->SectorSize
;
192 RtlCopyMemory(Ptr
, DiskReadBuffer
, Length
);
196 SectorOffset
+= ReadSectors
;
197 TotalSectors
-= ReadSectors
;
200 *Count
= (ULONG
)((ULONG_PTR
)Ptr
- (ULONG_PTR
)Buffer
);
201 Context
->SectorNumber
= SectorOffset
- Context
->SectorOffset
;
203 return (!ret
) ? EIO
: ESUCCESS
;
207 DiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
209 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
210 LARGE_INTEGER NewPosition
= *Position
;
217 NewPosition
.QuadPart
+= (Context
->SectorNumber
* Context
->SectorSize
);
224 if (NewPosition
.QuadPart
& (Context
->SectorSize
- 1))
227 /* Convert in number of sectors */
228 NewPosition
.QuadPart
/= Context
->SectorSize
;
230 /* HACK: CDROMs may have a SectorCount of 0 */
231 if (Context
->SectorCount
!= 0 && NewPosition
.QuadPart
>= Context
->SectorCount
)
234 Context
->SectorNumber
= NewPosition
.QuadPart
;
238 static const DEVVTBL DiskVtbl
=
241 DiskGetFileInformation
,
249 GetHarddiskIdentifier(UCHAR DriveNumber
)
251 return PcDiskIdentifier
[DriveNumber
- 0x80];
255 GetHarddiskInformation(UCHAR DriveNumber
)
257 PMASTER_BOOT_RECORD Mbr
;
262 BOOLEAN ValidPartitionTable
;
263 CHAR ArcName
[MAX_PATH
];
264 PARTITION_TABLE_ENTRY PartitionTableEntry
;
265 PCHAR Identifier
= PcDiskIdentifier
[DriveNumber
- 0x80];
267 /* Detect disk partition type */
268 DiskDetectPartitionType(DriveNumber
);
271 if (!MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
273 ERR("Reading MBR failed\n");
274 /* We failed, use a default identifier */
275 sprintf(Identifier
, "BIOSDISK%d", DriveNumber
- 0x80 + 1);
279 Buffer
= (ULONG
*)DiskReadBuffer
;
280 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
282 Signature
= Mbr
->Signature
;
283 TRACE("Signature: %x\n", Signature
);
285 /* Calculate the MBR checksum */
287 for (i
= 0; i
< 512 / sizeof(ULONG
); i
++)
289 Checksum
+= Buffer
[i
];
291 Checksum
= ~Checksum
+ 1;
292 TRACE("Checksum: %x\n", Checksum
);
294 ValidPartitionTable
= (Mbr
->MasterBootRecordMagic
== 0xAA55);
296 /* Fill out the ARC disk block */
297 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)", DriveNumber
- 0x80);
298 AddReactOSArcDiskInfo(ArcName
, Signature
, Checksum
, ValidPartitionTable
);
300 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber
- 0x80);
301 FsRegisterDevice(ArcName
, &DiskVtbl
);
305 DiskReportError(FALSE
);
306 while (DiskGetPartitionEntry(DriveNumber
, i
, &PartitionTableEntry
))
308 if (PartitionTableEntry
.SystemIndicator
!= PARTITION_ENTRY_UNUSED
)
310 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber
- 0x80, i
);
311 FsRegisterDevice(ArcName
, &DiskVtbl
);
315 DiskReportError(TRUE
);
317 /* Convert checksum and signature to identifier string */
318 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
319 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
320 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
321 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
322 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
323 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
324 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
325 Identifier
[7] = Hex
[Checksum
& 0x0F];
327 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
328 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
329 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
330 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
331 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
332 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
333 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
334 Identifier
[16] = Hex
[Signature
& 0x0F];
335 Identifier
[17] = '-';
336 Identifier
[18] = (ValidPartitionTable
? 'A' : 'X');
338 TRACE("Identifier: %s\n", Identifier
);
342 EnumerateHarddisks(OUT PBOOLEAN BootDriveReported
)
344 UCHAR DiskCount
, DriveNumber
;
348 *BootDriveReported
= FALSE
;
350 /* Count the number of visible harddisk drives */
351 DiskReportError(FALSE
);
355 ASSERT(DiskReadBufferSize
> 0);
358 * There are some really broken BIOSes out there. There are even BIOSes
359 * that happily report success when you ask them to read from non-existent
360 * harddisks. So, we set the buffer to known contents first, then try to
361 * read. If the BIOS reports success but the buffer contents haven't
362 * changed then we fail anyway.
364 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
365 while (MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
368 for (i
= 0; !Changed
&& i
< DiskReadBufferSize
; i
++)
370 Changed
= ((PUCHAR
)DiskReadBuffer
)[i
] != 0xcd;
374 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
375 (int)DiskCount
, DriveNumber
);
379 /* Cache the BIOS hard disk information for later use */
380 GetHarddiskInformation(DriveNumber
);
382 /* Check if we have seen the boot drive */
383 if (FrldrBootDrive
== DriveNumber
)
384 *BootDriveReported
= TRUE
;
388 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
390 DiskReportError(TRUE
);
392 PcBiosDiskCount
= DiskCount
;
393 TRACE("BIOS reports %d harddisk%s\n",
394 (int)DiskCount
, (DiskCount
== 1) ? "" : "s");
399 // FIXME: Copied from pcdisk.c
400 // Actually this function is REALLY PC-specific!!
402 DiskIsDriveRemovable(UCHAR DriveNumber
)
405 * Hard disks use drive numbers >= 0x80 . So if the drive number
406 * indicates a hard disk then return FALSE.
407 * 0x49 is our magic ramdisk drive, so return FALSE for that too.
409 if ((DriveNumber
>= 0x80) || (DriveNumber
== 0x49))
412 /* The drive is a floppy diskette so return TRUE */
417 DiskGetBootPath(BOOLEAN IsPxe
)
422 // FIXME! FIXME! Do this in some drive recognition procedure!!!!
425 RtlStringCbCopyA(FrLdrBootPath
, sizeof(FrLdrBootPath
), "net(0)");
428 /* 0x49 is our magic ramdisk drive, so try to detect it first */
429 if (FrldrBootDrive
== 0x49)
431 /* This is the ramdisk. See ArmInitializeBootDevices() too... */
432 // RtlStringCbPrintfA(FrLdrBootPath, sizeof(FrLdrBootPath), "ramdisk(%u)", 0);
433 RtlStringCbCopyA(FrLdrBootPath
, sizeof(FrLdrBootPath
), "ramdisk(0)");
435 else if (FrldrBootDrive
< 0x80)
437 /* This is a floppy */
438 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
439 "multi(0)disk(0)fdisk(%u)", FrldrBootDrive
);
441 else if (FrldrBootPartition
== 0xFF)
443 /* Boot Partition 0xFF is the magic value that indicates booting from CD-ROM (see isoboot.S) */
444 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
445 "multi(0)disk(0)cdrom(%u)", FrldrBootDrive
- 0x80);
450 PARTITION_TABLE_ENTRY PartitionEntry
;
452 /* This is a hard disk */
453 if (!DiskGetBootPartitionEntry(FrldrBootDrive
, &PartitionEntry
, &BootPartition
))
455 ERR("Failed to get boot partition entry\n");
459 FrldrBootPartition
= BootPartition
;
461 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
462 "multi(0)disk(0)rdisk(%u)partition(%lu)",
463 FrldrBootDrive
- 0x80, FrldrBootPartition
);
470 PcInitializeBootDevices(VOID
)
473 BOOLEAN BootDriveReported
= FALSE
;
476 DiskCount
= EnumerateHarddisks(&BootDriveReported
);
478 /* Initialize FrLdrBootPath, the boot path we're booting from (the "SystemPartition") */
479 DiskGetBootPath(PxeInit());
481 /* Add it, if it's a floppy or cdrom */
482 if ((FrldrBootDrive
>= 0x80 && !BootDriveReported
) ||
483 DiskIsDriveRemovable(FrldrBootDrive
))
485 /* TODO: Check if it's really a CDROM drive */
487 PMASTER_BOOT_RECORD Mbr
;
493 if (!MachDiskReadLogicalSectors(FrldrBootDrive
, 16ULL, 1, DiskReadBuffer
))
495 ERR("Reading MBR failed\n");
499 Buffer
= (ULONG
*)DiskReadBuffer
;
500 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
502 Signature
= Mbr
->Signature
;
503 TRACE("Signature: %x\n", Signature
);
505 /* Calculate the MBR checksum */
506 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++)
508 Checksum
+= Buffer
[i
];
510 Checksum
= ~Checksum
+ 1;
511 TRACE("Checksum: %x\n", Checksum
);
513 /* Fill out the ARC disk block */
514 AddReactOSArcDiskInfo(FrLdrBootPath
, Signature
, Checksum
, TRUE
);
516 FsRegisterDevice(FrLdrBootPath
, &DiskVtbl
);
517 DiskCount
++; // This is not accounted for in the number of pre-enumerated BIOS drives!
518 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive
);
521 return (DiskCount
!= 0);