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.
26 DBG_DEFAULT_CHANNEL(HWDETECT
);
29 * This is the common code for harddisk for both the PC and the XBOX.
32 typedef struct tagDISKCONTEXT
36 ULONGLONG SectorOffset
;
37 ULONGLONG SectorCount
;
38 ULONGLONG SectorNumber
;
41 static const CHAR Hex
[] = "0123456789abcdef";
43 /* Data cache for BIOS disks pre-enumeration */
44 UCHAR PcBiosDiskCount
= 0;
45 static CHAR PcDiskIdentifier
[32][20];
48 SIZE_T DiskReadBufferSize
;
51 /* FUNCTIONS *****************************************************************/
54 DiskClose(ULONG FileId
)
56 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
58 FrLdrTempFree(Context
, TAG_HW_DISK_CONTEXT
);
63 DiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
65 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
67 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
68 Information
->EndingAddress
.QuadPart
= Context
->SectorCount
* Context
->SectorSize
;
69 Information
->CurrentAddress
.QuadPart
= Context
->SectorNumber
* Context
->SectorSize
;
75 DiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
79 ULONG DrivePartition
, SectorSize
;
80 ULONGLONG SectorOffset
= 0;
81 ULONGLONG SectorCount
= 0;
82 PARTITION_TABLE_ENTRY PartitionTableEntry
;
85 if (DiskReadBufferSize
== 0)
87 ERR("DiskOpen(): DiskReadBufferSize is 0, something is wrong.\n");
92 if (!DissectArcPath(Path
, FileName
, &DriveNumber
, &DrivePartition
))
95 if (DrivePartition
== 0xff)
97 /* This is a CD-ROM device */
103 * This is either a floppy disk device (DrivePartition == 0) or
104 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF)
105 * but it doesn't matter which one because they both have 512 bytes
111 if (DrivePartition
!= 0xff && DrivePartition
!= 0)
113 if (!DiskGetPartitionEntry(DriveNumber
, DrivePartition
, &PartitionTableEntry
))
116 SectorOffset
= PartitionTableEntry
.SectorCountBeforePartition
;
117 SectorCount
= PartitionTableEntry
.PartitionSectorCount
;
119 #if 0 // FIXME: Investigate
122 SectorCount
= 0; /* FIXME */
126 Context
= FrLdrTempAlloc(sizeof(DISKCONTEXT
), TAG_HW_DISK_CONTEXT
);
130 Context
->DriveNumber
= DriveNumber
;
131 Context
->SectorSize
= SectorSize
;
132 Context
->SectorOffset
= SectorOffset
;
133 Context
->SectorCount
= SectorCount
;
134 Context
->SectorNumber
= 0;
135 FsSetDeviceSpecific(*FileId
, Context
);
141 DiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
143 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
144 UCHAR
* Ptr
= (UCHAR
*)Buffer
;
145 ULONG Length
, TotalSectors
, MaxSectors
, ReadSectors
;
147 ULONGLONG SectorOffset
;
149 ASSERT(DiskReadBufferSize
> 0);
151 TotalSectors
= (N
+ Context
->SectorSize
- 1) / Context
->SectorSize
;
152 MaxSectors
= DiskReadBufferSize
/ Context
->SectorSize
;
153 SectorOffset
= Context
->SectorNumber
+ Context
->SectorOffset
;
155 // If MaxSectors is 0, this will lead to infinite loop
156 // In release builds assertions are disabled, however we also have sanity checks in DiskOpen()
157 ASSERT(MaxSectors
> 0);
163 ReadSectors
= TotalSectors
;
164 if (ReadSectors
> MaxSectors
)
165 ReadSectors
= MaxSectors
;
167 ret
= MachDiskReadLogicalSectors(Context
->DriveNumber
,
174 Length
= ReadSectors
* Context
->SectorSize
;
178 RtlCopyMemory(Ptr
, DiskReadBuffer
, Length
);
182 SectorOffset
+= ReadSectors
;
183 TotalSectors
-= ReadSectors
;
186 *Count
= (ULONG
)(Ptr
- (UCHAR
*)Buffer
);
188 return (!ret
) ? EIO
: ESUCCESS
;
192 DiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
194 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
196 if (SeekMode
!= SeekAbsolute
)
198 if (Position
->LowPart
& (Context
->SectorSize
- 1))
201 Context
->SectorNumber
= Position
->QuadPart
/ Context
->SectorSize
;
205 static const DEVVTBL DiskVtbl
=
208 DiskGetFileInformation
,
216 GetHarddiskIdentifier(UCHAR DriveNumber
)
218 return PcDiskIdentifier
[DriveNumber
- 0x80];
222 GetHarddiskInformation(UCHAR DriveNumber
)
224 PMASTER_BOOT_RECORD Mbr
;
229 BOOLEAN ValidPartitionTable
;
230 CHAR ArcName
[MAX_PATH
];
231 PARTITION_TABLE_ENTRY PartitionTableEntry
;
232 PCHAR Identifier
= PcDiskIdentifier
[DriveNumber
- 0x80];
234 /* Detect disk partition type */
235 DiskDetectPartitionType(DriveNumber
);
238 if (!MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
240 ERR("Reading MBR failed\n");
241 /* We failed, use a default identifier */
242 sprintf(Identifier
, "BIOSDISK%d", DriveNumber
- 0x80 + 1);
246 Buffer
= (ULONG
*)DiskReadBuffer
;
247 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
249 Signature
= Mbr
->Signature
;
250 TRACE("Signature: %x\n", Signature
);
252 /* Calculate the MBR checksum */
254 for (i
= 0; i
< 512 / sizeof(ULONG
); i
++)
256 Checksum
+= Buffer
[i
];
258 Checksum
= ~Checksum
+ 1;
259 TRACE("Checksum: %x\n", Checksum
);
261 ValidPartitionTable
= (Mbr
->MasterBootRecordMagic
== 0xAA55);
263 /* Fill out the ARC disk block */
264 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)", DriveNumber
- 0x80);
265 AddReactOSArcDiskInfo(ArcName
, Signature
, Checksum
, ValidPartitionTable
);
267 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber
- 0x80);
268 FsRegisterDevice(ArcName
, &DiskVtbl
);
272 DiskReportError(FALSE
);
273 while (DiskGetPartitionEntry(DriveNumber
, i
, &PartitionTableEntry
))
275 if (PartitionTableEntry
.SystemIndicator
!= PARTITION_ENTRY_UNUSED
)
277 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber
- 0x80, i
);
278 FsRegisterDevice(ArcName
, &DiskVtbl
);
282 DiskReportError(TRUE
);
284 /* Convert checksum and signature to identifier string */
285 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
286 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
287 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
288 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
289 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
290 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
291 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
292 Identifier
[7] = Hex
[Checksum
& 0x0F];
294 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
295 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
296 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
297 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
298 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
299 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
300 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
301 Identifier
[16] = Hex
[Signature
& 0x0F];
302 Identifier
[17] = '-';
303 Identifier
[18] = (ValidPartitionTable
? 'A' : 'X');
305 TRACE("Identifier: %s\n", Identifier
);
309 EnumerateHarddisks(OUT PBOOLEAN BootDriveReported
)
311 UCHAR DiskCount
, DriveNumber
;
315 *BootDriveReported
= FALSE
;
317 /* Count the number of visible harddisk drives */
318 DiskReportError(FALSE
);
323 * There are some really broken BIOSes out there. There are even BIOSes
324 * that happily report success when you ask them to read from non-existent
325 * harddisks. So, we set the buffer to known contents first, then try to
326 * read. If the BIOS reports success but the buffer contents haven't
327 * changed then we fail anyway.
329 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
330 while (MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
333 for (i
= 0; !Changed
&& i
< DiskReadBufferSize
; i
++)
335 Changed
= ((PUCHAR
)DiskReadBuffer
)[i
] != 0xcd;
339 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
340 (int)DiskCount
, DriveNumber
);
344 /* Cache the BIOS hard disk information for later use */
345 GetHarddiskInformation(DriveNumber
);
347 /* Check if we have seen the boot drive */
348 if (FrldrBootDrive
== DriveNumber
)
349 *BootDriveReported
= TRUE
;
353 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
355 DiskReportError(TRUE
);
357 PcBiosDiskCount
= DiskCount
;
358 TRACE("BIOS reports %d harddisk%s\n",
359 (int)DiskCount
, (DiskCount
== 1) ? "" : "s");
365 PcInitializeBootDevices(VOID
)
368 BOOLEAN BootDriveReported
= FALSE
;
370 CHAR BootPath
[MAX_PATH
];
372 DiskCount
= EnumerateHarddisks(&BootDriveReported
);
374 /* Get the drive we're booting from */
375 MachDiskGetBootPath(BootPath
, sizeof(BootPath
));
377 /* Add it, if it's a floppy or cdrom */
378 if ((FrldrBootDrive
>= 0x80 && !BootDriveReported
) ||
379 DiskIsDriveRemovable(FrldrBootDrive
))
381 /* TODO: Check if it's really a CDROM drive */
383 PMASTER_BOOT_RECORD Mbr
;
389 if (!MachDiskReadLogicalSectors(FrldrBootDrive
, 16ULL, 1, DiskReadBuffer
))
391 ERR("Reading MBR failed\n");
395 Buffer
= (ULONG
*)DiskReadBuffer
;
396 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
398 Signature
= Mbr
->Signature
;
399 TRACE("Signature: %x\n", Signature
);
401 /* Calculate the MBR checksum */
402 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++)
404 Checksum
+= Buffer
[i
];
406 Checksum
= ~Checksum
+ 1;
407 TRACE("Checksum: %x\n", Checksum
);
409 /* Fill out the ARC disk block */
410 AddReactOSArcDiskInfo(BootPath
, Signature
, Checksum
, TRUE
);
412 FsRegisterDevice(BootPath
, &DiskVtbl
);
413 DiskCount
++; // This is not accounted for in the number of pre-enumerated BIOS drives!
414 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive
);
417 return (DiskCount
!= 0);