Sync with trunk (r47116), hopefully without breaking anything.
[reactos.git] / boot / freeldr / freeldr / arch / i386 / xboxhw.c
1 /* $Id$
2 *
3 * FreeLoader
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <freeldr.h>
21
22 #define NDEBUG
23 #include <debug.h>
24
25 static CHAR Hex[] = "0123456789ABCDEF";
26 //static unsigned int delay_count = 1;
27
28 extern ULONG reactos_disk_count;
29 extern ARC_DISK_SIGNATURE reactos_arc_disk_info[];
30 extern char reactos_arc_strings[32][256];
31
32 static PCM_PARTIAL_RESOURCE_LIST
33 GetHarddiskConfigurationData(ULONG DriveNumber, ULONG* pSize)
34 {
35 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
36 PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry;
37 EXTENDED_GEOMETRY ExtGeometry;
38 GEOMETRY Geometry;
39 ULONG Size;
40
41 //
42 // Initialize returned size
43 //
44 *pSize = 0;
45
46 /* Set 'Configuration Data' value */
47 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
48 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
49 PartialResourceList = MmHeapAlloc(Size);
50 if (PartialResourceList == NULL)
51 {
52 DPRINTM(DPRINT_HWDETECT,
53 "Failed to allocate a full resource descriptor\n");
54 return NULL;
55 }
56
57 memset(PartialResourceList, 0, Size);
58 PartialResourceList->Version = 1;
59 PartialResourceList->Revision = 1;
60 PartialResourceList->Count = 1;
61 PartialResourceList->PartialDescriptors[0].Type =
62 CmResourceTypeDeviceSpecific;
63 // PartialResourceList->PartialDescriptors[0].ShareDisposition =
64 // PartialResourceList->PartialDescriptors[0].Flags =
65 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
66 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
67
68 /* Get pointer to geometry data */
69 DiskGeometry = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
70
71 /* Get the disk geometry */
72 ExtGeometry.Size = sizeof(EXTENDED_GEOMETRY);
73
74 if(MachDiskGetDriveGeometry(DriveNumber, &Geometry))
75 {
76 DiskGeometry->BytesPerSector = Geometry.BytesPerSector;
77 DiskGeometry->NumberOfCylinders = Geometry.Cylinders;
78 DiskGeometry->SectorsPerTrack = Geometry.Sectors;
79 DiskGeometry->NumberOfHeads = Geometry.Heads;
80 }
81 else
82 {
83 DPRINTM(DPRINT_HWDETECT, "Reading disk geometry failed\n");
84 MmHeapFree(PartialResourceList);
85 return NULL;
86 }
87 DPRINTM(DPRINT_HWDETECT,
88 "Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
89 DriveNumber,
90 DiskGeometry->NumberOfCylinders,
91 DiskGeometry->NumberOfHeads,
92 DiskGeometry->SectorsPerTrack,
93 DiskGeometry->BytesPerSector);
94
95 //
96 // Return configuration data
97 //
98 *pSize = Size;
99 return PartialResourceList;
100 }
101
102 typedef struct tagDISKCONTEXT
103 {
104 ULONG DriveNumber;
105 ULONG SectorSize;
106 ULONGLONG SectorOffset;
107 ULONGLONG SectorCount;
108 ULONGLONG SectorNumber;
109 } DISKCONTEXT;
110
111 static LONG DiskClose(ULONG FileId)
112 {
113 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
114
115 MmHeapFree(Context);
116 return ESUCCESS;
117 }
118
119 static LONG DiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
120 {
121 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
122
123 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
124 Information->EndingAddress.QuadPart = (Context->SectorOffset + Context->SectorCount) * Context->SectorSize;
125 Information->CurrentAddress.LowPart = (Context->SectorOffset + Context->SectorNumber) * Context->SectorSize;
126
127 return ESUCCESS;
128 }
129
130 static LONG DiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
131 {
132 DISKCONTEXT* Context;
133 ULONG DriveNumber, DrivePartition, SectorSize;
134 ULONGLONG SectorOffset = 0;
135 ULONGLONG SectorCount = 0;
136 PARTITION_TABLE_ENTRY PartitionTableEntry;
137 CHAR FileName[1];
138
139 if (!DissectArcPath(Path, FileName, &DriveNumber, &DrivePartition))
140 return EINVAL;
141
142 if (DrivePartition == 0xff)
143 {
144 /* This is a CD-ROM device */
145 SectorSize = 2048;
146 }
147 else
148 {
149 /* This is either a floppy disk device (DrivePartition == 0) or
150 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF) but
151 * it doesn't matter which one because they both have 512 bytes per sector */
152 SectorSize = 512;
153 }
154
155 if (DrivePartition != 0xff && DrivePartition != 0)
156 {
157 if (!XboxDiskGetPartitionEntry(DriveNumber, DrivePartition, &PartitionTableEntry))
158 return EINVAL;
159 SectorOffset = PartitionTableEntry.SectorCountBeforePartition;
160 SectorCount = PartitionTableEntry.PartitionSectorCount;
161 }
162 else
163 {
164 SectorCount = 0; /* FIXME */
165 }
166
167 Context = MmHeapAlloc(sizeof(DISKCONTEXT));
168 if (!Context)
169 return ENOMEM;
170 Context->DriveNumber = DriveNumber;
171 Context->SectorSize = SectorSize;
172 Context->SectorOffset = SectorOffset;
173 Context->SectorCount = SectorCount;
174 Context->SectorNumber = 0;
175 FsSetDeviceSpecific(*FileId, Context);
176
177 return ESUCCESS;
178 }
179
180 static LONG DiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
181 {
182 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
183 UCHAR* Ptr = (UCHAR*)Buffer;
184 ULONG i, Length, Sectors;
185 BOOLEAN ret;
186
187 *Count = 0;
188 i = 0;
189 while (N > 0)
190 {
191 Length = N;
192 if (Length > DISKREADBUFFER_SIZE)
193 Length = DISKREADBUFFER_SIZE;
194 Sectors = (Length + Context->SectorSize - 1) / Context->SectorSize;
195 ret = MachDiskReadLogicalSectors(
196 Context->DriveNumber,
197 Context->SectorNumber + Context->SectorOffset + i,
198 Sectors,
199 (PVOID)DISKREADBUFFER);
200 if (!ret)
201 return EIO;
202 RtlCopyMemory(Ptr, (PVOID)DISKREADBUFFER, Length);
203 Ptr += Length;
204 *Count += Length;
205 N -= Length;
206 i += Sectors;
207 }
208
209 return ESUCCESS;
210 }
211
212 static LONG DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
213 {
214 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
215
216 if (SeekMode != SeekAbsolute)
217 return EINVAL;
218 if (Position->LowPart & (Context->SectorSize - 1))
219 return EINVAL;
220
221 /* FIXME: take HighPart into account */
222 Context->SectorNumber = Position->LowPart / Context->SectorSize;
223 return ESUCCESS;
224 }
225
226 static const DEVVTBL DiskVtbl = {
227 DiskClose,
228 DiskGetFileInformation,
229 DiskOpen,
230 DiskRead,
231 DiskSeek,
232 };
233
234 static VOID
235 GetHarddiskIdentifier(PCHAR Identifier,
236 ULONG DriveNumber)
237 {
238 PMASTER_BOOT_RECORD Mbr;
239 ULONG *Buffer;
240 ULONG i;
241 ULONG Checksum;
242 ULONG Signature;
243 CHAR ArcName[256];
244 PARTITION_TABLE_ENTRY PartitionTableEntry;
245
246 /* Read the MBR */
247 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
248 {
249 DPRINTM(DPRINT_HWDETECT, "Reading MBR failed\n");
250 return;
251 }
252
253 Buffer = (ULONG*)DISKREADBUFFER;
254 Mbr = (PMASTER_BOOT_RECORD)DISKREADBUFFER;
255
256 Signature = Mbr->Signature;
257 DPRINTM(DPRINT_HWDETECT, "Signature: %x\n", Signature);
258
259 /* Calculate the MBR checksum */
260 Checksum = 0;
261 for (i = 0; i < 128; i++)
262 {
263 Checksum += Buffer[i];
264 }
265 Checksum = ~Checksum + 1;
266 DPRINTM(DPRINT_HWDETECT, "Checksum: %x\n", Checksum);
267
268 /* Fill out the ARC disk block */
269 reactos_arc_disk_info[reactos_disk_count].Signature = Signature;
270 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
271 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)", reactos_disk_count);
272 strcpy(reactos_arc_strings[reactos_disk_count], ArcName);
273 reactos_arc_disk_info[reactos_disk_count].ArcName =
274 reactos_arc_strings[reactos_disk_count];
275 reactos_disk_count++;
276
277 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)partition(0)", DriveNumber - 0x80);
278 FsRegisterDevice(ArcName, &DiskVtbl);
279
280 /* Add partitions */
281 i = 1;
282 DiskReportError(FALSE);
283 while (XboxDiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
284 {
285 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
286 {
287 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)partition(%lu)", DriveNumber - 0x80, i);
288 FsRegisterDevice(ArcName, &DiskVtbl);
289 }
290 i++;
291 }
292 DiskReportError(TRUE);
293
294 /* Convert checksum and signature to identifier string */
295 Identifier[0] = Hex[(Checksum >> 28) & 0x0F];
296 Identifier[1] = Hex[(Checksum >> 24) & 0x0F];
297 Identifier[2] = Hex[(Checksum >> 20) & 0x0F];
298 Identifier[3] = Hex[(Checksum >> 16) & 0x0F];
299 Identifier[4] = Hex[(Checksum >> 12) & 0x0F];
300 Identifier[5] = Hex[(Checksum >> 8) & 0x0F];
301 Identifier[6] = Hex[(Checksum >> 4) & 0x0F];
302 Identifier[7] = Hex[Checksum & 0x0F];
303 Identifier[8] = '-';
304 Identifier[9] = Hex[(Signature >> 28) & 0x0F];
305 Identifier[10] = Hex[(Signature >> 24) & 0x0F];
306 Identifier[11] = Hex[(Signature >> 20) & 0x0F];
307 Identifier[12] = Hex[(Signature >> 16) & 0x0F];
308 Identifier[13] = Hex[(Signature >> 12) & 0x0F];
309 Identifier[14] = Hex[(Signature >> 8) & 0x0F];
310 Identifier[15] = Hex[(Signature >> 4) & 0x0F];
311 Identifier[16] = Hex[Signature & 0x0F];
312 Identifier[17] = '-';
313 Identifier[18] = 'A';
314 Identifier[19] = 0;
315 DPRINTM(DPRINT_HWDETECT, "Identifier: %s\n", Identifier);
316 }
317
318 static VOID
319 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
320 PCONFIGURATION_COMPONENT_DATA BusKey)
321 {
322 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
323 PCM_INT13_DRIVE_PARAMETER Int13Drives;
324 GEOMETRY Geometry;
325 PCONFIGURATION_COMPONENT_DATA DiskKey, ControllerKey;
326 ULONG DiskCount;
327 ULONG Size;
328 ULONG i;
329 BOOLEAN Changed;
330
331 /* Count the number of visible drives */
332 DiskReportError(FALSE);
333 DiskCount = 0;
334
335 /* There are some really broken BIOSes out there. There are even BIOSes
336 * that happily report success when you ask them to read from non-existent
337 * harddisks. So, we set the buffer to known contents first, then try to
338 * read. If the BIOS reports success but the buffer contents haven't
339 * changed then we fail anyway */
340 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
341 while (MachDiskReadLogicalSectors(0x80 + DiskCount, 0ULL, 1, (PVOID)DISKREADBUFFER))
342 {
343 Changed = FALSE;
344 for (i = 0; ! Changed && i < 512; i++)
345 {
346 Changed = ((PUCHAR)DISKREADBUFFER)[i] != 0xcd;
347 }
348 if (! Changed)
349 {
350 DPRINTM(DPRINT_HWDETECT, "BIOS reports success for disk %d but data didn't change\n",
351 (int)DiskCount);
352 break;
353 }
354 DiskCount++;
355 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
356 }
357 DiskReportError(TRUE);
358 DPRINTM(DPRINT_HWDETECT, "BIOS reports %d harddisk%s\n",
359 (int)DiskCount, (DiskCount == 1) ? "": "s");
360
361 //DetectBiosFloppyController(BusKey);
362
363 /* Allocate resource descriptor */
364 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
365 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
366 PartialResourceList = MmHeapAlloc(Size);
367 if (PartialResourceList == NULL)
368 {
369 DPRINTM(DPRINT_HWDETECT,
370 "Failed to allocate resource descriptor\n");
371 return;
372 }
373
374 /* Initialize resource descriptor */
375 memset(PartialResourceList, 0, Size);
376 PartialResourceList->Version = 1;
377 PartialResourceList->Revision = 1;
378 PartialResourceList->Count = 1;
379 PartialResourceList->PartialDescriptors[0].Type = CmResourceTypeDeviceSpecific;
380 PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
381 PartialResourceList->PartialDescriptors[0].Flags = 0;
382 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
383 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
384
385 /* Get harddisk Int13 geometry data */
386 Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
387 for (i = 0; i < DiskCount; i++)
388 {
389 if (MachDiskGetDriveGeometry(0x80 + i, &Geometry))
390 {
391 Int13Drives[i].DriveSelect = 0x80 + i;
392 Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
393 Int13Drives[i].SectorsPerTrack = Geometry.Sectors;
394 Int13Drives[i].MaxHeads = Geometry.Heads - 1;
395 Int13Drives[i].NumberDrives = DiskCount;
396
397 DPRINTM(DPRINT_HWDETECT,
398 "Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
399 0x80 + i,
400 Geometry.Cylinders - 1,
401 Geometry.Heads -1,
402 Geometry.Sectors,
403 Geometry.BytesPerSector);
404 }
405 }
406
407 FldrCreateComponentKey(BusKey,
408 ControllerClass,
409 DiskController,
410 Output | Input,
411 0,
412 0xFFFFFFFF,
413 NULL,
414 PartialResourceList,
415 Size,
416 &ControllerKey);
417 DPRINTM(DPRINT_HWDETECT, "Created key: DiskController\\0\n");
418
419 MmHeapFree(PartialResourceList);
420
421 /* Create and fill subkey for each harddisk */
422 for (i = 0; i < DiskCount; i++)
423 {
424 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
425 ULONG Size;
426 CHAR Identifier[20];
427
428 /* Get disk values */
429 PartialResourceList = GetHarddiskConfigurationData(0x80 + i, &Size);
430 GetHarddiskIdentifier(Identifier, 0x80 + i);
431
432 /* Create disk key */
433 FldrCreateComponentKey(ControllerKey,
434 PeripheralClass,
435 DiskPeripheral,
436 Output | Input,
437 0,
438 0xFFFFFFFF,
439 Identifier,
440 PartialResourceList,
441 Size,
442 &DiskKey);
443
444 if (PartialResourceList)
445 MmHeapFree(PartialResourceList);
446 }
447 }
448
449 static VOID
450 DetectIsaBios(PCONFIGURATION_COMPONENT_DATA SystemKey, ULONG *BusNumber)
451 {
452 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
453 PCONFIGURATION_COMPONENT_DATA BusKey;
454 ULONG Size;
455
456 /* Set 'Configuration Data' value */
457 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) -
458 sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
459 PartialResourceList = MmHeapAlloc(Size);
460 if (PartialResourceList == NULL)
461 {
462 DPRINTM(DPRINT_HWDETECT,
463 "Failed to allocate resource descriptor\n");
464 return;
465 }
466
467 /* Initialize resource descriptor */
468 memset(PartialResourceList, 0, Size);
469 PartialResourceList->Version = 1;
470 PartialResourceList->Revision = 1;
471 PartialResourceList->Count = 0;
472
473 /* Create new bus key */
474 FldrCreateComponentKey(SystemKey,
475 AdapterClass,
476 MultiFunctionAdapter,
477 0x0,
478 0x0,
479 0xFFFFFFFF,
480 "ISA",
481 PartialResourceList,
482 Size,
483 &BusKey);
484
485 /* Increment bus number */
486 (*BusNumber)++;
487
488 MmHeapFree(PartialResourceList);
489
490 /* Detect ISA/BIOS devices */
491 DetectBiosDisks(SystemKey, BusKey);
492
493
494 /* FIXME: Detect more ISA devices */
495 }
496
497 PCONFIGURATION_COMPONENT_DATA
498 XboxHwDetect(VOID)
499 {
500 PCONFIGURATION_COMPONENT_DATA SystemKey;
501 ULONG BusNumber = 0;
502
503 DPRINTM(DPRINT_HWDETECT, "DetectHardware()\n");
504
505 /* Create the 'System' key */
506 FldrCreateSystemKey(&SystemKey);
507
508 /* TODO: Build actual xbox's hardware configuration tree */
509 DetectIsaBios(SystemKey, &BusNumber);
510
511 DPRINTM(DPRINT_HWDETECT, "DetectHardware() Done\n");
512 return SystemKey;
513 }
514
515 /* EOF */