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 extern ULONG reactos_disk_count
;
42 extern ARC_DISK_SIGNATURE_EX reactos_arc_disk_info
[];
44 static CHAR Hex
[] = "0123456789abcdef";
46 /* Data cache for BIOS disks pre-enumeration */
47 UCHAR PcBiosDiskCount
= 0;
48 static CHAR PcDiskIdentifier
[32][20];
51 SIZE_T DiskReadBufferSize
;
54 /* FUNCTIONS *****************************************************************/
57 DiskClose(ULONG FileId
)
59 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
61 FrLdrTempFree(Context
, TAG_HW_DISK_CONTEXT
);
66 DiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
68 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
70 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
71 Information
->EndingAddress
.QuadPart
= (Context
->SectorOffset
+ Context
->SectorCount
) * Context
->SectorSize
;
72 Information
->CurrentAddress
.QuadPart
= (Context
->SectorOffset
+ Context
->SectorNumber
) * Context
->SectorSize
;
78 DiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
82 ULONG DrivePartition
, SectorSize
;
83 ULONGLONG SectorOffset
= 0;
84 ULONGLONG SectorCount
= 0;
85 PARTITION_TABLE_ENTRY PartitionTableEntry
;
88 if (!DissectArcPath(Path
, FileName
, &DriveNumber
, &DrivePartition
))
91 if (DrivePartition
== 0xff)
93 /* This is a CD-ROM device */
99 * This is either a floppy disk device (DrivePartition == 0) or
100 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF)
101 * but it doesn't matter which one because they both have 512 bytes
107 if (DrivePartition
!= 0xff && DrivePartition
!= 0)
109 if (!DiskGetPartitionEntry(DriveNumber
, DrivePartition
, &PartitionTableEntry
))
112 SectorOffset
= PartitionTableEntry
.SectorCountBeforePartition
;
113 SectorCount
= PartitionTableEntry
.PartitionSectorCount
;
115 #if 0 // FIXME: Investigate
118 SectorCount
= 0; /* FIXME */
122 Context
= FrLdrTempAlloc(sizeof(DISKCONTEXT
), TAG_HW_DISK_CONTEXT
);
126 Context
->DriveNumber
= DriveNumber
;
127 Context
->SectorSize
= SectorSize
;
128 Context
->SectorOffset
= SectorOffset
;
129 Context
->SectorCount
= SectorCount
;
130 Context
->SectorNumber
= 0;
131 FsSetDeviceSpecific(*FileId
, Context
);
137 DiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
139 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
140 UCHAR
* Ptr
= (UCHAR
*)Buffer
;
141 ULONG Length
, TotalSectors
, MaxSectors
, ReadSectors
;
143 ULONGLONG SectorOffset
;
145 TotalSectors
= (N
+ Context
->SectorSize
- 1) / Context
->SectorSize
;
146 MaxSectors
= DiskReadBufferSize
/ Context
->SectorSize
;
147 SectorOffset
= Context
->SectorNumber
+ Context
->SectorOffset
;
153 ReadSectors
= TotalSectors
;
154 if (ReadSectors
> MaxSectors
)
155 ReadSectors
= MaxSectors
;
157 ret
= MachDiskReadLogicalSectors(Context
->DriveNumber
,
164 Length
= ReadSectors
* Context
->SectorSize
;
168 RtlCopyMemory(Ptr
, DiskReadBuffer
, Length
);
172 SectorOffset
+= ReadSectors
;
173 TotalSectors
-= ReadSectors
;
176 *Count
= (ULONG
)(Ptr
- (UCHAR
*)Buffer
);
178 return (!ret
) ? EIO
: ESUCCESS
;
182 DiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
184 DISKCONTEXT
* Context
= FsGetDeviceSpecific(FileId
);
186 if (SeekMode
!= SeekAbsolute
)
188 if (Position
->LowPart
& (Context
->SectorSize
- 1))
191 Context
->SectorNumber
= Position
->QuadPart
/ Context
->SectorSize
;
195 static const DEVVTBL DiskVtbl
=
198 DiskGetFileInformation
,
206 GetHarddiskIdentifier(UCHAR DriveNumber
)
208 return PcDiskIdentifier
[DriveNumber
- 0x80];
212 GetHarddiskInformation(UCHAR DriveNumber
)
214 PMASTER_BOOT_RECORD Mbr
;
219 CHAR ArcName
[MAX_PATH
];
220 PARTITION_TABLE_ENTRY PartitionTableEntry
;
221 PCHAR Identifier
= PcDiskIdentifier
[DriveNumber
- 0x80];
224 if (!MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
226 ERR("Reading MBR failed\n");
230 Buffer
= (ULONG
*)DiskReadBuffer
;
231 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
233 Signature
= Mbr
->Signature
;
234 TRACE("Signature: %x\n", Signature
);
236 /* Calculate the MBR checksum */
238 for (i
= 0; i
< 512 / sizeof(ULONG
); i
++)
240 Checksum
+= Buffer
[i
];
242 Checksum
= ~Checksum
+ 1;
243 TRACE("Checksum: %x\n", Checksum
);
245 /* Fill out the ARC disk block */
246 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.Signature
= Signature
;
247 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.CheckSum
= Checksum
;
248 sprintf(ArcName
, "multi(0)disk(0)rdisk(%lu)", reactos_disk_count
);
249 strcpy(reactos_arc_disk_info
[reactos_disk_count
].ArcName
, ArcName
);
250 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.ArcName
=
251 reactos_arc_disk_info
[reactos_disk_count
].ArcName
;
252 reactos_disk_count
++;
254 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber
- 0x80);
255 FsRegisterDevice(ArcName
, &DiskVtbl
);
259 DiskReportError(FALSE
);
260 while (DiskGetPartitionEntry(DriveNumber
, i
, &PartitionTableEntry
))
262 if (PartitionTableEntry
.SystemIndicator
!= PARTITION_ENTRY_UNUSED
)
264 sprintf(ArcName
, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber
- 0x80, i
);
265 FsRegisterDevice(ArcName
, &DiskVtbl
);
269 DiskReportError(TRUE
);
271 /* Convert checksum and signature to identifier string */
272 Identifier
[0] = Hex
[(Checksum
>> 28) & 0x0F];
273 Identifier
[1] = Hex
[(Checksum
>> 24) & 0x0F];
274 Identifier
[2] = Hex
[(Checksum
>> 20) & 0x0F];
275 Identifier
[3] = Hex
[(Checksum
>> 16) & 0x0F];
276 Identifier
[4] = Hex
[(Checksum
>> 12) & 0x0F];
277 Identifier
[5] = Hex
[(Checksum
>> 8) & 0x0F];
278 Identifier
[6] = Hex
[(Checksum
>> 4) & 0x0F];
279 Identifier
[7] = Hex
[Checksum
& 0x0F];
281 Identifier
[9] = Hex
[(Signature
>> 28) & 0x0F];
282 Identifier
[10] = Hex
[(Signature
>> 24) & 0x0F];
283 Identifier
[11] = Hex
[(Signature
>> 20) & 0x0F];
284 Identifier
[12] = Hex
[(Signature
>> 16) & 0x0F];
285 Identifier
[13] = Hex
[(Signature
>> 12) & 0x0F];
286 Identifier
[14] = Hex
[(Signature
>> 8) & 0x0F];
287 Identifier
[15] = Hex
[(Signature
>> 4) & 0x0F];
288 Identifier
[16] = Hex
[Signature
& 0x0F];
289 Identifier
[17] = '-';
290 Identifier
[18] = 'A'; // FIXME: Not always 'A' ...
292 TRACE("Identifier: %s\n", Identifier
);
296 PcInitializeBootDevices(VOID
)
298 UCHAR DiskCount
, DriveNumber
;
301 BOOLEAN BootDriveReported
= FALSE
;
302 CHAR BootPath
[MAX_PATH
];
304 /* Count the number of visible drives */
305 DiskReportError(FALSE
);
310 * There are some really broken BIOSes out there. There are even BIOSes
311 * that happily report success when you ask them to read from non-existent
312 * harddisks. So, we set the buffer to known contents first, then try to
313 * read. If the BIOS reports success but the buffer contents haven't
314 * changed then we fail anyway.
316 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
317 while (MachDiskReadLogicalSectors(DriveNumber
, 0ULL, 1, DiskReadBuffer
))
320 for (i
= 0; !Changed
&& i
< DiskReadBufferSize
; i
++)
322 Changed
= ((PUCHAR
)DiskReadBuffer
)[i
] != 0xcd;
326 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
327 (int)DiskCount
, DriveNumber
);
331 /* Cache the BIOS hard disk information for later use */
332 GetHarddiskInformation(DriveNumber
);
334 /* Check if we have seen the boot drive */
335 if (FrldrBootDrive
== DriveNumber
)
336 BootDriveReported
= TRUE
;
340 memset(DiskReadBuffer
, 0xcd, DiskReadBufferSize
);
342 DiskReportError(TRUE
);
344 PcBiosDiskCount
= DiskCount
;
345 TRACE("BIOS reports %d harddisk%s\n",
346 (int)DiskCount
, (DiskCount
== 1) ? "" : "s");
348 /* Get the drive we're booting from */
349 MachDiskGetBootPath(BootPath
, sizeof(BootPath
));
351 /* Add it, if it's a floppy or cdrom */
352 if ((FrldrBootDrive
>= 0x80 && !BootDriveReported
) ||
353 DiskIsDriveRemovable(FrldrBootDrive
))
355 /* TODO: Check if it's really a CDROM drive */
357 PMASTER_BOOT_RECORD Mbr
;
363 if (!MachDiskReadLogicalSectors(FrldrBootDrive
, 16ULL, 1, DiskReadBuffer
))
365 ERR("Reading MBR failed\n");
369 Buffer
= (ULONG
*)DiskReadBuffer
;
370 Mbr
= (PMASTER_BOOT_RECORD
)DiskReadBuffer
;
372 Signature
= Mbr
->Signature
;
373 TRACE("Signature: %x\n", Signature
);
375 /* Calculate the MBR checksum */
376 for (i
= 0; i
< 2048 / sizeof(ULONG
); i
++) Checksum
+= Buffer
[i
];
377 Checksum
= ~Checksum
+ 1;
378 TRACE("Checksum: %x\n", Checksum
);
380 /* Fill out the ARC disk block */
381 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.Signature
= Signature
;
382 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.CheckSum
= Checksum
;
383 strcpy(reactos_arc_disk_info
[reactos_disk_count
].ArcName
, BootPath
);
384 reactos_arc_disk_info
[reactos_disk_count
].DiskSignature
.ArcName
=
385 reactos_arc_disk_info
[reactos_disk_count
].ArcName
;
386 reactos_disk_count
++;
388 FsRegisterDevice(BootPath
, &DiskVtbl
);
389 DiskCount
++; // This is not accounted for in the number of pre-enumerated BIOS drives!
390 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive
);
393 return (DiskCount
!= 0);