[FREELDR] Simplify code by using a AddReactOSArcDiskInfo() helper, and few extra...
[reactos.git] / boot / freeldr / freeldr / arch / i386 / hwdisk.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2003, 2004 Eric Kohl
5 * Copyright (C) 2009 Hervé Poussineau
6 *
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.
11 *
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.
16 *
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.
20 */
21
22 #include <freeldr.h>
23
24 #include <debug.h>
25
26 DBG_DEFAULT_CHANNEL(HWDETECT);
27
28 /*
29 * This is the common code for harddisk for both the PC and the XBOX.
30 */
31
32 typedef struct tagDISKCONTEXT
33 {
34 UCHAR DriveNumber;
35 ULONG SectorSize;
36 ULONGLONG SectorOffset;
37 ULONGLONG SectorCount;
38 ULONGLONG SectorNumber;
39 } DISKCONTEXT;
40
41 static CHAR Hex[] = "0123456789abcdef";
42
43 /* Data cache for BIOS disks pre-enumeration */
44 UCHAR PcBiosDiskCount = 0;
45 static CHAR PcDiskIdentifier[32][20];
46
47 PVOID DiskReadBuffer;
48 SIZE_T DiskReadBufferSize;
49
50
51 /* FUNCTIONS *****************************************************************/
52
53 static ARC_STATUS
54 DiskClose(ULONG FileId)
55 {
56 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
57
58 FrLdrTempFree(Context, TAG_HW_DISK_CONTEXT);
59 return ESUCCESS;
60 }
61
62 static ARC_STATUS
63 DiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
64 {
65 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
66
67 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
68 Information->EndingAddress.QuadPart = (Context->SectorOffset + Context->SectorCount) * Context->SectorSize;
69 Information->CurrentAddress.QuadPart = (Context->SectorOffset + Context->SectorNumber) * Context->SectorSize;
70
71 return ESUCCESS;
72 }
73
74 static ARC_STATUS
75 DiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
76 {
77 DISKCONTEXT* Context;
78 UCHAR DriveNumber;
79 ULONG DrivePartition, SectorSize;
80 ULONGLONG SectorOffset = 0;
81 ULONGLONG SectorCount = 0;
82 PARTITION_TABLE_ENTRY PartitionTableEntry;
83 CHAR FileName[1];
84
85 if (!DissectArcPath(Path, FileName, &DriveNumber, &DrivePartition))
86 return EINVAL;
87
88 if (DrivePartition == 0xff)
89 {
90 /* This is a CD-ROM device */
91 SectorSize = 2048;
92 }
93 else
94 {
95 /*
96 * This is either a floppy disk device (DrivePartition == 0) or
97 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF)
98 * but it doesn't matter which one because they both have 512 bytes
99 * per sector.
100 */
101 SectorSize = 512;
102 }
103
104 if (DrivePartition != 0xff && DrivePartition != 0)
105 {
106 if (!DiskGetPartitionEntry(DriveNumber, DrivePartition, &PartitionTableEntry))
107 return EINVAL;
108
109 SectorOffset = PartitionTableEntry.SectorCountBeforePartition;
110 SectorCount = PartitionTableEntry.PartitionSectorCount;
111 }
112 #if 0 // FIXME: Investigate
113 else
114 {
115 SectorCount = 0; /* FIXME */
116 }
117 #endif
118
119 Context = FrLdrTempAlloc(sizeof(DISKCONTEXT), TAG_HW_DISK_CONTEXT);
120 if (!Context)
121 return ENOMEM;
122
123 Context->DriveNumber = DriveNumber;
124 Context->SectorSize = SectorSize;
125 Context->SectorOffset = SectorOffset;
126 Context->SectorCount = SectorCount;
127 Context->SectorNumber = 0;
128 FsSetDeviceSpecific(*FileId, Context);
129
130 return ESUCCESS;
131 }
132
133 static ARC_STATUS
134 DiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
135 {
136 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
137 UCHAR* Ptr = (UCHAR*)Buffer;
138 ULONG Length, TotalSectors, MaxSectors, ReadSectors;
139 BOOLEAN ret;
140 ULONGLONG SectorOffset;
141
142 TotalSectors = (N + Context->SectorSize - 1) / Context->SectorSize;
143 MaxSectors = DiskReadBufferSize / Context->SectorSize;
144 SectorOffset = Context->SectorNumber + Context->SectorOffset;
145
146 ret = TRUE;
147
148 while (TotalSectors)
149 {
150 ReadSectors = TotalSectors;
151 if (ReadSectors > MaxSectors)
152 ReadSectors = MaxSectors;
153
154 ret = MachDiskReadLogicalSectors(Context->DriveNumber,
155 SectorOffset,
156 ReadSectors,
157 DiskReadBuffer);
158 if (!ret)
159 break;
160
161 Length = ReadSectors * Context->SectorSize;
162 if (Length > N)
163 Length = N;
164
165 RtlCopyMemory(Ptr, DiskReadBuffer, Length);
166
167 Ptr += Length;
168 N -= Length;
169 SectorOffset += ReadSectors;
170 TotalSectors -= ReadSectors;
171 }
172
173 *Count = (ULONG)(Ptr - (UCHAR*)Buffer);
174
175 return (!ret) ? EIO : ESUCCESS;
176 }
177
178 static ARC_STATUS
179 DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
180 {
181 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
182
183 if (SeekMode != SeekAbsolute)
184 return EINVAL;
185 if (Position->LowPart & (Context->SectorSize - 1))
186 return EINVAL;
187
188 Context->SectorNumber = Position->QuadPart / Context->SectorSize;
189 return ESUCCESS;
190 }
191
192 static const DEVVTBL DiskVtbl =
193 {
194 DiskClose,
195 DiskGetFileInformation,
196 DiskOpen,
197 DiskRead,
198 DiskSeek,
199 };
200
201
202 PCHAR
203 GetHarddiskIdentifier(UCHAR DriveNumber)
204 {
205 return PcDiskIdentifier[DriveNumber - 0x80];
206 }
207
208 static VOID
209 GetHarddiskInformation(UCHAR DriveNumber)
210 {
211 PMASTER_BOOT_RECORD Mbr;
212 PULONG Buffer;
213 ULONG i;
214 ULONG Checksum;
215 ULONG Signature;
216 BOOLEAN ValidPartitionTable;
217 CHAR ArcName[MAX_PATH];
218 PARTITION_TABLE_ENTRY PartitionTableEntry;
219 PCHAR Identifier = PcDiskIdentifier[DriveNumber - 0x80];
220
221 /* Read the MBR */
222 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, DiskReadBuffer))
223 {
224 ERR("Reading MBR failed\n");
225 /* We failed, use a default identifier */
226 sprintf(Identifier, "BIOSDISK%d", DriveNumber - 0x80 + 1);
227 return;
228 }
229
230 Buffer = (ULONG*)DiskReadBuffer;
231 Mbr = (PMASTER_BOOT_RECORD)DiskReadBuffer;
232
233 Signature = Mbr->Signature;
234 TRACE("Signature: %x\n", Signature);
235
236 /* Calculate the MBR checksum */
237 Checksum = 0;
238 for (i = 0; i < 512 / sizeof(ULONG); i++)
239 {
240 Checksum += Buffer[i];
241 }
242 Checksum = ~Checksum + 1;
243 TRACE("Checksum: %x\n", Checksum);
244
245 ValidPartitionTable = (Mbr->MasterBootRecordMagic == 0xAA55);
246
247 /* Fill out the ARC disk block */
248 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)", DriveNumber - 0x80);
249 AddReactOSArcDiskInfo(ArcName, Signature, Checksum, ValidPartitionTable);
250
251 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber - 0x80);
252 FsRegisterDevice(ArcName, &DiskVtbl);
253
254 /* Add partitions */
255 i = 1;
256 DiskReportError(FALSE);
257 while (DiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
258 {
259 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
260 {
261 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber - 0x80, i);
262 FsRegisterDevice(ArcName, &DiskVtbl);
263 }
264 i++;
265 }
266 DiskReportError(TRUE);
267
268 /* Convert checksum and signature to identifier string */
269 Identifier[0] = Hex[(Checksum >> 28) & 0x0F];
270 Identifier[1] = Hex[(Checksum >> 24) & 0x0F];
271 Identifier[2] = Hex[(Checksum >> 20) & 0x0F];
272 Identifier[3] = Hex[(Checksum >> 16) & 0x0F];
273 Identifier[4] = Hex[(Checksum >> 12) & 0x0F];
274 Identifier[5] = Hex[(Checksum >> 8) & 0x0F];
275 Identifier[6] = Hex[(Checksum >> 4) & 0x0F];
276 Identifier[7] = Hex[Checksum & 0x0F];
277 Identifier[8] = '-';
278 Identifier[9] = Hex[(Signature >> 28) & 0x0F];
279 Identifier[10] = Hex[(Signature >> 24) & 0x0F];
280 Identifier[11] = Hex[(Signature >> 20) & 0x0F];
281 Identifier[12] = Hex[(Signature >> 16) & 0x0F];
282 Identifier[13] = Hex[(Signature >> 12) & 0x0F];
283 Identifier[14] = Hex[(Signature >> 8) & 0x0F];
284 Identifier[15] = Hex[(Signature >> 4) & 0x0F];
285 Identifier[16] = Hex[Signature & 0x0F];
286 Identifier[17] = '-';
287 Identifier[18] = (ValidPartitionTable ? 'A' : 'X');
288 Identifier[19] = 0;
289 TRACE("Identifier: %s\n", Identifier);
290 }
291
292 static UCHAR
293 EnumerateHarddisks(OUT PBOOLEAN BootDriveReported)
294 {
295 UCHAR DiskCount, DriveNumber;
296 ULONG i;
297 BOOLEAN Changed;
298
299 *BootDriveReported = FALSE;
300
301 /* Count the number of visible harddisk drives */
302 DiskReportError(FALSE);
303 DiskCount = 0;
304 DriveNumber = 0x80;
305
306 /*
307 * There are some really broken BIOSes out there. There are even BIOSes
308 * that happily report success when you ask them to read from non-existent
309 * harddisks. So, we set the buffer to known contents first, then try to
310 * read. If the BIOS reports success but the buffer contents haven't
311 * changed then we fail anyway.
312 */
313 memset(DiskReadBuffer, 0xcd, DiskReadBufferSize);
314 while (MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, DiskReadBuffer))
315 {
316 Changed = FALSE;
317 for (i = 0; !Changed && i < DiskReadBufferSize; i++)
318 {
319 Changed = ((PUCHAR)DiskReadBuffer)[i] != 0xcd;
320 }
321 if (!Changed)
322 {
323 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
324 (int)DiskCount, DriveNumber);
325 break;
326 }
327
328 /* Cache the BIOS hard disk information for later use */
329 GetHarddiskInformation(DriveNumber);
330
331 /* Check if we have seen the boot drive */
332 if (FrldrBootDrive == DriveNumber)
333 *BootDriveReported = TRUE;
334
335 DiskCount++;
336 DriveNumber++;
337 memset(DiskReadBuffer, 0xcd, DiskReadBufferSize);
338 }
339 DiskReportError(TRUE);
340
341 PcBiosDiskCount = DiskCount;
342 TRACE("BIOS reports %d harddisk%s\n",
343 (int)DiskCount, (DiskCount == 1) ? "" : "s");
344
345 return DiskCount;
346 }
347
348 BOOLEAN
349 PcInitializeBootDevices(VOID)
350 {
351 UCHAR DiskCount;
352 BOOLEAN BootDriveReported = FALSE;
353 ULONG i;
354 CHAR BootPath[MAX_PATH];
355
356 DiskCount = EnumerateHarddisks(&BootDriveReported);
357
358 /* Get the drive we're booting from */
359 MachDiskGetBootPath(BootPath, sizeof(BootPath));
360
361 /* Add it, if it's a floppy or cdrom */
362 if ((FrldrBootDrive >= 0x80 && !BootDriveReported) ||
363 DiskIsDriveRemovable(FrldrBootDrive))
364 {
365 /* TODO: Check if it's really a CDROM drive */
366
367 PMASTER_BOOT_RECORD Mbr;
368 PULONG Buffer;
369 ULONG Checksum = 0;
370 ULONG Signature;
371
372 /* Read the MBR */
373 if (!MachDiskReadLogicalSectors(FrldrBootDrive, 16ULL, 1, DiskReadBuffer))
374 {
375 ERR("Reading MBR failed\n");
376 return FALSE;
377 }
378
379 Buffer = (ULONG*)DiskReadBuffer;
380 Mbr = (PMASTER_BOOT_RECORD)DiskReadBuffer;
381
382 Signature = Mbr->Signature;
383 TRACE("Signature: %x\n", Signature);
384
385 /* Calculate the MBR checksum */
386 for (i = 0; i < 2048 / sizeof(ULONG); i++)
387 {
388 Checksum += Buffer[i];
389 }
390 Checksum = ~Checksum + 1;
391 TRACE("Checksum: %x\n", Checksum);
392
393 /* Fill out the ARC disk block */
394 AddReactOSArcDiskInfo(BootPath, Signature, Checksum, TRUE);
395
396 FsRegisterDevice(BootPath, &DiskVtbl);
397 DiskCount++; // This is not accounted for in the number of pre-enumerated BIOS drives!
398 TRACE("Additional boot drive detected: 0x%02X\n", (int)FrldrBootDrive);
399 }
400
401 return (DiskCount != 0);
402 }