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
));
66 Information
->EndingAddress
.QuadPart
= Context
->SectorCount
* Context
->SectorSize
;
67 Information
->CurrentAddress
.QuadPart
= Context
->SectorNumber
* Context
->SectorSize
;
73 DiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
77 ULONG DrivePartition
, SectorSize
;
78 ULONGLONG SectorOffset
= 0;
79 ULONGLONG SectorCount
= 0;
80 PARTITION_TABLE_ENTRY PartitionTableEntry
;
82 if (DiskReadBufferSize
== 0)
84 ERR("DiskOpen(): DiskReadBufferSize is 0, something is wrong.\n");
89 if (!DissectArcPath(Path
, NULL
, &DriveNumber
, &DrivePartition
))
92 if (DrivePartition
== 0xff)
94 /* This is a CD-ROM device */
100 * This is either a floppy disk device (DrivePartition == 0) or
101 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF)
102 * but it doesn't matter which one because they both have 512 bytes
108 if (DrivePartition
!= 0xff && DrivePartition
!= 0)
110 if (!DiskGetPartitionEntry(DriveNumber
, DrivePartition
, &PartitionTableEntry
))
113 SectorOffset
= PartitionTableEntry
.SectorCountBeforePartition
;
114 SectorCount
= PartitionTableEntry
.PartitionSectorCount
;
116 #if 0 // FIXME: Investigate
119 SectorCount
= 0; /* FIXME */
123 Context
= FrLdrTempAlloc(sizeof(DISKCONTEXT
), TAG_HW_DISK_CONTEXT
);
127 Context
->DriveNumber
= DriveNumber
;
128 Context
->SectorSize
= SectorSize
;
129 Context
->SectorOffset
= SectorOffset
;
130 Context
->SectorCount
= SectorCount
;
131 Context
->SectorNumber
= 0;
132 FsSetDeviceSpecific(*FileId
, Context
);
138 DiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
140 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
141 UCHAR
* Ptr
= (UCHAR
*)Buffer
;
142 ULONG Length
, TotalSectors
, MaxSectors
, ReadSectors
;
144 ULONGLONG SectorOffset
;
146 ASSERT(DiskReadBufferSize
> 0);
148 TotalSectors
= (N
+ Context
->SectorSize
- 1) / Context
->SectorSize
;
149 MaxSectors
= DiskReadBufferSize
/ Context
->SectorSize
;
150 SectorOffset
= Context
->SectorNumber
+ Context
->SectorOffset
;
152 // If MaxSectors is 0, this will lead to infinite loop.
153 // In release builds assertions are disabled, however we also have sanity checks in DiskOpen()
154 ASSERT(MaxSectors
> 0);
160 ReadSectors
= TotalSectors
;
161 if (ReadSectors
> MaxSectors
)
162 ReadSectors
= MaxSectors
;
164 ret
= MachDiskReadLogicalSectors(Context
->DriveNumber
,
171 Length
= ReadSectors
* Context
->SectorSize
;
175 RtlCopyMemory(Ptr
, DiskReadBuffer
, Length
);
179 SectorOffset
+= ReadSectors
;
180 TotalSectors
-= ReadSectors
;
183 *Count
= (ULONG
)(Ptr
- (UCHAR
*)Buffer
);
185 return (!ret
) ? EIO
: ESUCCESS
;
189 DiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
191 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
193 if (SeekMode
!= SeekAbsolute
)
195 if (Position
->LowPart
& (Context
->SectorSize
- 1))
198 Context
->SectorNumber
= Position
->QuadPart
/ Context
->SectorSize
;
202 static const DEVVTBL DiskVtbl
=
205 DiskGetFileInformation
,
213 GetHarddiskIdentifier(UCHAR DriveNumber
)
215 return PcDiskIdentifier
[DriveNumber
- 0x80];
219 GetHarddiskInformation(UCHAR DriveNumber
)
221 PMASTER_BOOT_RECORD Mbr
;
226 BOOLEAN ValidPartitionTable
;
227 CHAR ArcName
[MAX_PATH
];
228 PARTITION_TABLE_ENTRY PartitionTableEntry
;
229 PCHAR Identifier
= PcDiskIdentifier
[DriveNumber
- 0x80];
231 /* Detect disk partition type */
232 DiskDetectPartitionType(DriveNumber
);
235 if (!MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
237 ERR("Reading MBR failed\n");
238 /* We failed, use a default identifier */
239 sprintf(Identifier
, "BIOSDISK%d", DriveNumber
- 0x80 + 1);
243 Buffer
= (ULONG
*)DiskReadBuffer
;
244 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
246 Signature
= Mbr
->Signature
;
247 TRACE("Signature: %x\n", Signature
);
249 /* Calculate the MBR checksum */
251 for (i
= 0; i
< 512 / sizeof(ULONG
); i
++)
253 Checksum
+= Buffer
[i
];
255 Checksum
= ~Checksum
+ 1;
256 TRACE("Checksum: %x\n", Checksum
);
258 ValidPartitionTable
= (Mbr
->MasterBootRecordMagic
== 0xAA55);
260 /* Fill out the ARC disk block */
261 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)", DriveNumber
- 0x80);
262 AddReactOSArcDiskInfo(ArcName
, Signature
, Checksum
, ValidPartitionTable
);
264 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber
- 0x80);
265 FsRegisterDevice(ArcName
, &DiskVtbl
);
269 DiskReportError(FALSE
);
270 while (DiskGetPartitionEntry(DriveNumber
, i
, &PartitionTableEntry
))
272 if (PartitionTableEntry
.SystemIndicator
!= PARTITION_ENTRY_UNUSED
)
274 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber
- 0x80, i
);
275 FsRegisterDevice(ArcName
, &DiskVtbl
);
279 DiskReportError(TRUE
);
281 /* Convert checksum and signature to identifier string */
282 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
283 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
284 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
285 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
286 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
287 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
288 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
289 Identifier
[7] = Hex
[Checksum
& 0x0F];
291 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
292 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
293 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
294 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
295 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
296 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
297 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
298 Identifier
[16] = Hex
[Signature
& 0x0F];
299 Identifier
[17] = '-';
300 Identifier
[18] = (ValidPartitionTable
? 'A' : 'X');
302 TRACE("Identifier: %s\n", Identifier
);
306 EnumerateHarddisks(OUT PBOOLEAN BootDriveReported
)
308 UCHAR DiskCount
, DriveNumber
;
312 *BootDriveReported
= FALSE
;
314 /* Count the number of visible harddisk drives */
315 DiskReportError(FALSE
);
319 ASSERT(DiskReadBufferSize
> 0);
322 * There are some really broken BIOSes out there. There are even BIOSes
323 * that happily report success when you ask them to read from non-existent
324 * harddisks. So, we set the buffer to known contents first, then try to
325 * read. If the BIOS reports success but the buffer contents haven't
326 * changed then we fail anyway.
328 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
329 while (MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
332 for (i
= 0; !Changed
&& i
< DiskReadBufferSize
; i
++)
334 Changed
= ((PUCHAR
)DiskReadBuffer
)[i
] != 0xcd;
338 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
339 (int)DiskCount
, DriveNumber
);
343 /* Cache the BIOS hard disk information for later use */
344 GetHarddiskInformation(DriveNumber
);
346 /* Check if we have seen the boot drive */
347 if (FrldrBootDrive
== DriveNumber
)
348 *BootDriveReported
= TRUE
;
352 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
354 DiskReportError(TRUE
);
356 PcBiosDiskCount
= DiskCount
;
357 TRACE("BIOS reports %d harddisk%s\n",
358 (int)DiskCount
, (DiskCount
== 1) ? "" : "s");
364 DiskIsDriveRemovable(UCHAR DriveNumber
)
367 * Hard disks use drive numbers >= 0x80 . So if the drive number
368 * indicates a hard disk then return FALSE.
369 * 0x49 is our magic ramdisk drive, so return FALSE for that too.
371 if ((DriveNumber
>= 0x80) || (DriveNumber
== 0x49))
374 /* The drive is a floppy diskette so return TRUE */
379 DiskGetBootPath(BOOLEAN IsPxe
)
384 // FIXME! FIXME! Do this in some drive recognition procedure!!!!
387 RtlStringCbCopyA(FrldrBootPath
, sizeof(FrldrBootPath
), "net(0)");
390 /* 0x49 is our magic ramdisk drive, so try to detect it first */
391 if (FrldrBootDrive
== 0x49)
393 /* This is the ramdisk. See ArmInitializeBootDevices() too... */
394 // RtlStringCbPrintfA(FrldrBootPath, sizeof(FrldrBootPath), "ramdisk(%u)", 0);
395 RtlStringCbCopyA(FrldrBootPath
, sizeof(FrldrBootPath
), "ramdisk(0)");
397 else if (FrldrBootDrive
< 0x80)
399 /* This is a floppy */
400 RtlStringCbPrintfA(FrldrBootPath
, sizeof(FrldrBootPath
),
401 "multi(0)disk(0)fdisk(%u)", FrldrBootDrive
);
403 else if (FrldrBootPartition
== 0xFF)
405 /* Boot Partition 0xFF is the magic value that indicates booting from CD-ROM (see isoboot.S) */
406 RtlStringCbPrintfA(FrldrBootPath
, sizeof(FrldrBootPath
),
407 "multi(0)disk(0)cdrom(%u)", FrldrBootDrive
- 0x80);
412 PARTITION_TABLE_ENTRY PartitionEntry
;
414 /* This is a hard disk */
415 if (!DiskGetBootPartitionEntry(FrldrBootDrive
, &PartitionEntry
, &BootPartition
))
417 ERR("Failed to get boot partition entry\n");
421 FrldrBootPartition
= BootPartition
;
423 RtlStringCbPrintfA(FrldrBootPath
, sizeof(FrldrBootPath
),
424 "multi(0)disk(0)rdisk(%u)partition(%lu)",
425 FrldrBootDrive
- 0x80, FrldrBootPartition
);
432 PcInitializeBootDevices(VOID
)
435 BOOLEAN BootDriveReported
= FALSE
;
438 DiskCount
= EnumerateHarddisks(&BootDriveReported
);
440 /* Initialize FrldrBootPath, the boot path we're booting from (the "SystemPartition") */
441 DiskGetBootPath(PxeInit());
443 /* Add it, if it's a floppy or cdrom */
444 if ((FrldrBootDrive
>= 0x80 && !BootDriveReported
) ||
445 DiskIsDriveRemovable(FrldrBootDrive
))
447 /* TODO: Check if it's really a CDROM drive */
449 PMASTER_BOOT_RECORD Mbr
;
455 if (!MachDiskReadLogicalSectors(FrldrBootDrive
, 16ULL, 1, DiskReadBuffer
))
457 ERR("Reading MBR failed\n");
461 Buffer
= (ULONG
*)DiskReadBuffer
;
462 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
464 Signature
= Mbr
->Signature
;
465 TRACE("Signature: %x\n", Signature
);
467 /* Calculate the MBR checksum */
468 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++)
470 Checksum
+= Buffer
[i
];
472 Checksum
= ~Checksum
+ 1;
473 TRACE("Checksum: %x\n", Checksum
);
475 /* Fill out the ARC disk block */
476 AddReactOSArcDiskInfo(FrldrBootPath
, Signature
, Checksum
, TRUE
);
478 FsRegisterDevice(FrldrBootPath
, &DiskVtbl
);
479 DiskCount
++; // This is not accounted for in the number of pre-enumerated BIOS drives!
480 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive
);
483 return (DiskCount
!= 0);