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
;
161 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
)(Ptr
- (UCHAR
*)Buffer
);
202 return (!ret
) ? EIO
: ESUCCESS
;
206 DiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
208 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
209 LARGE_INTEGER NewPosition
= *Position
;
216 NewPosition
.QuadPart
+= (Context
->SectorNumber
* Context
->SectorSize
);
223 if (NewPosition
.QuadPart
& (Context
->SectorSize
- 1))
226 /* Convert in number of sectors */
227 NewPosition
.QuadPart
/= Context
->SectorSize
;
229 /* HACK: CDROMs may have a SectorCount of 0 */
230 if (Context
->SectorCount
!= 0 && NewPosition
.QuadPart
>= Context
->SectorCount
)
233 Context
->SectorNumber
= NewPosition
.QuadPart
;
237 static const DEVVTBL DiskVtbl
=
240 DiskGetFileInformation
,
248 GetHarddiskIdentifier(UCHAR DriveNumber
)
250 return PcDiskIdentifier
[DriveNumber
- 0x80];
254 GetHarddiskInformation(UCHAR DriveNumber
)
256 PMASTER_BOOT_RECORD Mbr
;
261 BOOLEAN ValidPartitionTable
;
262 CHAR ArcName
[MAX_PATH
];
263 PARTITION_TABLE_ENTRY PartitionTableEntry
;
264 PCHAR Identifier
= PcDiskIdentifier
[DriveNumber
- 0x80];
266 /* Detect disk partition type */
267 DiskDetectPartitionType(DriveNumber
);
270 if (!MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
272 ERR("Reading MBR failed\n");
273 /* We failed, use a default identifier */
274 sprintf(Identifier
, "BIOSDISK%d", DriveNumber
- 0x80 + 1);
278 Buffer
= (ULONG
*)DiskReadBuffer
;
279 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
281 Signature
= Mbr
->Signature
;
282 TRACE("Signature: %x\n", Signature
);
284 /* Calculate the MBR checksum */
286 for (i
= 0; i
< 512 / sizeof(ULONG
); i
++)
288 Checksum
+= Buffer
[i
];
290 Checksum
= ~Checksum
+ 1;
291 TRACE("Checksum: %x\n", Checksum
);
293 ValidPartitionTable
= (Mbr
->MasterBootRecordMagic
== 0xAA55);
295 /* Fill out the ARC disk block */
296 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)", DriveNumber
- 0x80);
297 AddReactOSArcDiskInfo(ArcName
, Signature
, Checksum
, ValidPartitionTable
);
299 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber
- 0x80);
300 FsRegisterDevice(ArcName
, &DiskVtbl
);
304 DiskReportError(FALSE
);
305 while (DiskGetPartitionEntry(DriveNumber
, i
, &PartitionTableEntry
))
307 if (PartitionTableEntry
.SystemIndicator
!= PARTITION_ENTRY_UNUSED
)
309 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber
- 0x80, i
);
310 FsRegisterDevice(ArcName
, &DiskVtbl
);
314 DiskReportError(TRUE
);
316 /* Convert checksum and signature to identifier string */
317 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
318 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
319 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
320 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
321 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
322 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
323 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
324 Identifier
[7] = Hex
[Checksum
& 0x0F];
326 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
327 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
328 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
329 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
330 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
331 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
332 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
333 Identifier
[16] = Hex
[Signature
& 0x0F];
334 Identifier
[17] = '-';
335 Identifier
[18] = (ValidPartitionTable
? 'A' : 'X');
337 TRACE("Identifier: %s\n", Identifier
);
341 EnumerateHarddisks(OUT PBOOLEAN BootDriveReported
)
343 UCHAR DiskCount
, DriveNumber
;
347 *BootDriveReported
= FALSE
;
349 /* Count the number of visible harddisk drives */
350 DiskReportError(FALSE
);
354 ASSERT(DiskReadBufferSize
> 0);
357 * There are some really broken BIOSes out there. There are even BIOSes
358 * that happily report success when you ask them to read from non-existent
359 * harddisks. So, we set the buffer to known contents first, then try to
360 * read. If the BIOS reports success but the buffer contents haven't
361 * changed then we fail anyway.
363 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
364 while (MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
367 for (i
= 0; !Changed
&& i
< DiskReadBufferSize
; i
++)
369 Changed
= ((PUCHAR
)DiskReadBuffer
)[i
] != 0xcd;
373 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
374 (int)DiskCount
, DriveNumber
);
378 /* Cache the BIOS hard disk information for later use */
379 GetHarddiskInformation(DriveNumber
);
381 /* Check if we have seen the boot drive */
382 if (FrldrBootDrive
== DriveNumber
)
383 *BootDriveReported
= TRUE
;
387 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
389 DiskReportError(TRUE
);
391 PcBiosDiskCount
= DiskCount
;
392 TRACE("BIOS reports %d harddisk%s\n",
393 (int)DiskCount
, (DiskCount
== 1) ? "" : "s");
399 DiskIsDriveRemovable(UCHAR DriveNumber
)
402 * Hard disks use drive numbers >= 0x80 . So if the drive number
403 * indicates a hard disk then return FALSE.
404 * 0x49 is our magic ramdisk drive, so return FALSE for that too.
406 if ((DriveNumber
>= 0x80) || (DriveNumber
== 0x49))
409 /* The drive is a floppy diskette so return TRUE */
414 DiskGetBootPath(BOOLEAN IsPxe
)
419 // FIXME! FIXME! Do this in some drive recognition procedure!!!!
422 RtlStringCbCopyA(FrLdrBootPath
, sizeof(FrLdrBootPath
), "net(0)");
425 /* 0x49 is our magic ramdisk drive, so try to detect it first */
426 if (FrldrBootDrive
== 0x49)
428 /* This is the ramdisk. See ArmInitializeBootDevices() too... */
429 // RtlStringCbPrintfA(FrLdrBootPath, sizeof(FrLdrBootPath), "ramdisk(%u)", 0);
430 RtlStringCbCopyA(FrLdrBootPath
, sizeof(FrLdrBootPath
), "ramdisk(0)");
432 else if (FrldrBootDrive
< 0x80)
434 /* This is a floppy */
435 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
436 "multi(0)disk(0)fdisk(%u)", FrldrBootDrive
);
438 else if (FrldrBootPartition
== 0xFF)
440 /* Boot Partition 0xFF is the magic value that indicates booting from CD-ROM (see isoboot.S) */
441 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
442 "multi(0)disk(0)cdrom(%u)", FrldrBootDrive
- 0x80);
447 PARTITION_TABLE_ENTRY PartitionEntry
;
449 /* This is a hard disk */
450 if (!DiskGetBootPartitionEntry(FrldrBootDrive
, &PartitionEntry
, &BootPartition
))
452 ERR("Failed to get boot partition entry\n");
456 FrldrBootPartition
= BootPartition
;
458 RtlStringCbPrintfA(FrLdrBootPath
, sizeof(FrLdrBootPath
),
459 "multi(0)disk(0)rdisk(%u)partition(%lu)",
460 FrldrBootDrive
- 0x80, FrldrBootPartition
);
467 PcInitializeBootDevices(VOID
)
470 BOOLEAN BootDriveReported
= FALSE
;
473 DiskCount
= EnumerateHarddisks(&BootDriveReported
);
475 /* Initialize FrLdrBootPath, the boot path we're booting from (the "SystemPartition") */
476 DiskGetBootPath(PxeInit());
478 /* Add it, if it's a floppy or cdrom */
479 if ((FrldrBootDrive
>= 0x80 && !BootDriveReported
) ||
480 DiskIsDriveRemovable(FrldrBootDrive
))
482 /* TODO: Check if it's really a CDROM drive */
484 PMASTER_BOOT_RECORD Mbr
;
490 if (!MachDiskReadLogicalSectors(FrldrBootDrive
, 16ULL, 1, DiskReadBuffer
))
492 ERR("Reading MBR failed\n");
496 Buffer
= (ULONG
*)DiskReadBuffer
;
497 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
499 Signature
= Mbr
->Signature
;
500 TRACE("Signature: %x\n", Signature
);
502 /* Calculate the MBR checksum */
503 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++)
505 Checksum
+= Buffer
[i
];
507 Checksum
= ~Checksum
+ 1;
508 TRACE("Checksum: %x\n", Checksum
);
510 /* Fill out the ARC disk block */
511 AddReactOSArcDiskInfo(FrLdrBootPath
, Signature
, Checksum
, TRUE
);
513 FsRegisterDevice(FrLdrBootPath
, &DiskVtbl
);
514 DiskCount
++; // This is not accounted for in the number of pre-enumerated BIOS drives!
515 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive
);
518 return (DiskCount
!= 0);