af86f62382eddcef22ea19e70f5d211e01836092
[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 extern ULONG reactos_disk_count;
42 extern ARC_DISK_SIGNATURE_EX reactos_arc_disk_info[];
43
44 static CHAR Hex[] = "0123456789abcdef";
45
46 /* Data cache for BIOS disks pre-enumeration */
47 UCHAR PcBiosDiskCount = 0;
48 static CHAR PcDiskIdentifier[32][20];
49
50 PVOID DiskReadBuffer;
51 SIZE_T DiskReadBufferSize;
52
53
54 /* FUNCTIONS *****************************************************************/
55
56 static ARC_STATUS
57 DiskClose(ULONG FileId)
58 {
59 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
60
61 FrLdrTempFree(Context, TAG_HW_DISK_CONTEXT);
62 return ESUCCESS;
63 }
64
65 static ARC_STATUS
66 DiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
67 {
68 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
69
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;
73
74 return ESUCCESS;
75 }
76
77 static ARC_STATUS
78 DiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
79 {
80 DISKCONTEXT* Context;
81 UCHAR DriveNumber;
82 ULONG DrivePartition, SectorSize;
83 ULONGLONG SectorOffset = 0;
84 ULONGLONG SectorCount = 0;
85 PARTITION_TABLE_ENTRY PartitionTableEntry;
86 CHAR FileName[1];
87
88 if (!DissectArcPath(Path, FileName, &DriveNumber, &DrivePartition))
89 return EINVAL;
90
91 if (DrivePartition == 0xff)
92 {
93 /* This is a CD-ROM device */
94 SectorSize = 2048;
95 }
96 else
97 {
98 /*
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
102 * per sector.
103 */
104 SectorSize = 512;
105 }
106
107 if (DrivePartition != 0xff && DrivePartition != 0)
108 {
109 if (!DiskGetPartitionEntry(DriveNumber, DrivePartition, &PartitionTableEntry))
110 return EINVAL;
111
112 SectorOffset = PartitionTableEntry.SectorCountBeforePartition;
113 SectorCount = PartitionTableEntry.PartitionSectorCount;
114 }
115 #if 0 // FIXME: Investigate
116 else
117 {
118 SectorCount = 0; /* FIXME */
119 }
120 #endif
121
122 Context = FrLdrTempAlloc(sizeof(DISKCONTEXT), TAG_HW_DISK_CONTEXT);
123 if (!Context)
124 return ENOMEM;
125
126 Context->DriveNumber = DriveNumber;
127 Context->SectorSize = SectorSize;
128 Context->SectorOffset = SectorOffset;
129 Context->SectorCount = SectorCount;
130 Context->SectorNumber = 0;
131 FsSetDeviceSpecific(*FileId, Context);
132
133 return ESUCCESS;
134 }
135
136 static ARC_STATUS
137 DiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
138 {
139 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
140 UCHAR* Ptr = (UCHAR*)Buffer;
141 ULONG Length, TotalSectors, MaxSectors, ReadSectors;
142 BOOLEAN ret;
143 ULONGLONG SectorOffset;
144
145 TotalSectors = (N + Context->SectorSize - 1) / Context->SectorSize;
146 MaxSectors = DiskReadBufferSize / Context->SectorSize;
147 SectorOffset = Context->SectorNumber + Context->SectorOffset;
148
149 ret = TRUE;
150
151 while (TotalSectors)
152 {
153 ReadSectors = TotalSectors;
154 if (ReadSectors > MaxSectors)
155 ReadSectors = MaxSectors;
156
157 ret = MachDiskReadLogicalSectors(Context->DriveNumber,
158 SectorOffset,
159 ReadSectors,
160 DiskReadBuffer);
161 if (!ret)
162 break;
163
164 Length = ReadSectors * Context->SectorSize;
165 if (Length > N)
166 Length = N;
167
168 RtlCopyMemory(Ptr, DiskReadBuffer, Length);
169
170 Ptr += Length;
171 N -= Length;
172 SectorOffset += ReadSectors;
173 TotalSectors -= ReadSectors;
174 }
175
176 *Count = (ULONG)(Ptr - (UCHAR*)Buffer);
177
178 return (!ret) ? EIO : ESUCCESS;
179 }
180
181 static ARC_STATUS
182 DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
183 {
184 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
185
186 if (SeekMode != SeekAbsolute)
187 return EINVAL;
188 if (Position->LowPart & (Context->SectorSize - 1))
189 return EINVAL;
190
191 Context->SectorNumber = Position->QuadPart / Context->SectorSize;
192 return ESUCCESS;
193 }
194
195 static const DEVVTBL DiskVtbl =
196 {
197 DiskClose,
198 DiskGetFileInformation,
199 DiskOpen,
200 DiskRead,
201 DiskSeek,
202 };
203
204
205 PCHAR
206 GetHarddiskIdentifier(UCHAR DriveNumber)
207 {
208 return PcDiskIdentifier[DriveNumber - 0x80];
209 }
210
211 static VOID
212 GetHarddiskInformation(UCHAR DriveNumber)
213 {
214 PMASTER_BOOT_RECORD Mbr;
215 PULONG Buffer;
216 ULONG i;
217 ULONG Checksum;
218 ULONG Signature;
219 CHAR ArcName[MAX_PATH];
220 PARTITION_TABLE_ENTRY PartitionTableEntry;
221 PCHAR Identifier = PcDiskIdentifier[DriveNumber - 0x80];
222
223 /* Read the MBR */
224 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, DiskReadBuffer))
225 {
226 ERR("Reading MBR failed\n");
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 /* 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++;
253
254 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber - 0x80);
255 FsRegisterDevice(ArcName, &DiskVtbl);
256
257 /* Add partitions */
258 i = 1;
259 DiskReportError(FALSE);
260 while (DiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
261 {
262 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
263 {
264 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber - 0x80, i);
265 FsRegisterDevice(ArcName, &DiskVtbl);
266 }
267 i++;
268 }
269 DiskReportError(TRUE);
270
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];
280 Identifier[8] = '-';
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' ...
291 Identifier[19] = 0;
292 TRACE("Identifier: %s\n", Identifier);
293 }
294
295 BOOLEAN
296 PcInitializeBootDevices(VOID)
297 {
298 UCHAR DiskCount, DriveNumber;
299 ULONG i;
300 BOOLEAN Changed;
301 BOOLEAN BootDriveReported = FALSE;
302 CHAR BootPath[MAX_PATH];
303
304 /* Count the number of visible drives */
305 DiskReportError(FALSE);
306 DiskCount = 0;
307 DriveNumber = 0x80;
308
309 /*
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.
315 */
316 memset(DiskReadBuffer, 0xcd, DiskReadBufferSize);
317 while (MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, DiskReadBuffer))
318 {
319 Changed = FALSE;
320 for (i = 0; !Changed && i < DiskReadBufferSize; i++)
321 {
322 Changed = ((PUCHAR)DiskReadBuffer)[i] != 0xcd;
323 }
324 if (!Changed)
325 {
326 TRACE("BIOS reports success for disk %d (0x%02X) but data didn't change\n",
327 (int)DiskCount, DriveNumber);
328 break;
329 }
330
331 /* Cache the BIOS hard disk information for later use */
332 GetHarddiskInformation(DriveNumber);
333
334 /* Check if we have seen the boot drive */
335 if (FrldrBootDrive == DriveNumber)
336 BootDriveReported = TRUE;
337
338 DiskCount++;
339 DriveNumber++;
340 memset(DiskReadBuffer, 0xcd, DiskReadBufferSize);
341 }
342 DiskReportError(TRUE);
343
344 PcBiosDiskCount = DiskCount;
345 TRACE("BIOS reports %d harddisk%s\n",
346 (int)DiskCount, (DiskCount == 1) ? "" : "s");
347
348 /* Get the drive we're booting from */
349 MachDiskGetBootPath(BootPath, sizeof(BootPath));
350
351 /* Add it, if it's a floppy or cdrom */
352 if ((FrldrBootDrive >= 0x80 && !BootDriveReported) ||
353 DiskIsDriveRemovable(FrldrBootDrive))
354 {
355 /* TODO: Check if it's really a CDROM drive */
356
357 PMASTER_BOOT_RECORD Mbr;
358 PULONG Buffer;
359 ULONG Checksum = 0;
360 ULONG Signature;
361
362 /* Read the MBR */
363 if (!MachDiskReadLogicalSectors(FrldrBootDrive, 16ULL, 1, DiskReadBuffer))
364 {
365 ERR("Reading MBR failed\n");
366 return FALSE;
367 }
368
369 Buffer = (ULONG*)DiskReadBuffer;
370 Mbr = (PMASTER_BOOT_RECORD)DiskReadBuffer;
371
372 Signature = Mbr->Signature;
373 TRACE("Signature: %x\n", Signature);
374
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);
379
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++;
387
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);
391 }
392
393 return (DiskCount != 0);
394 }