4b8597f30b9e845ed7ed37929692ac6bf8ee170e
[reactos.git] / reactos / 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 DBG_DEFAULT_CHANNEL(HWDETECT);
26
27 static CHAR Hex[] = "0123456789ABCDEF";
28 //static unsigned int delay_count = 1;
29
30 extern ULONG reactos_disk_count;
31 extern ARC_DISK_SIGNATURE reactos_arc_disk_info[];
32 extern char reactos_arc_strings[32][256];
33
34 static PCM_PARTIAL_RESOURCE_LIST
35 GetHarddiskConfigurationData(UCHAR DriveNumber, ULONG* pSize)
36 {
37 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
38 PCM_DISK_GEOMETRY_DEVICE_DATA DiskGeometry;
39 //EXTENDED_GEOMETRY ExtGeometry;
40 GEOMETRY Geometry;
41 ULONG Size;
42
43 //
44 // Initialize returned size
45 //
46 *pSize = 0;
47
48 /* Set 'Configuration Data' value */
49 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
50 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
51 PartialResourceList = MmHeapAlloc(Size);
52 if (PartialResourceList == NULL)
53 {
54 ERR("Failed to allocate a full resource descriptor\n");
55 return NULL;
56 }
57
58 memset(PartialResourceList, 0, Size);
59 PartialResourceList->Version = 1;
60 PartialResourceList->Revision = 1;
61 PartialResourceList->Count = 1;
62 PartialResourceList->PartialDescriptors[0].Type =
63 CmResourceTypeDeviceSpecific;
64 // PartialResourceList->PartialDescriptors[0].ShareDisposition =
65 // PartialResourceList->PartialDescriptors[0].Flags =
66 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
67 sizeof(CM_DISK_GEOMETRY_DEVICE_DATA);
68
69 /* Get pointer to geometry data */
70 DiskGeometry = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
71
72 /* Get the disk geometry */
73 //ExtGeometry.Size = sizeof(EXTENDED_GEOMETRY);
74
75 if(MachDiskGetDriveGeometry(DriveNumber, &Geometry))
76 {
77 DiskGeometry->BytesPerSector = Geometry.BytesPerSector;
78 DiskGeometry->NumberOfCylinders = Geometry.Cylinders;
79 DiskGeometry->SectorsPerTrack = Geometry.Sectors;
80 DiskGeometry->NumberOfHeads = Geometry.Heads;
81 }
82 else
83 {
84 ERR("Reading disk geometry failed\n");
85 MmHeapFree(PartialResourceList);
86 return NULL;
87 }
88 TRACE("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 UCHAR 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.QuadPart = (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 DrivePartition, SectorSize;
134 UCHAR DriveNumber;
135 ULONGLONG SectorOffset = 0;
136 ULONGLONG SectorCount = 0;
137 PARTITION_TABLE_ENTRY PartitionTableEntry;
138 CHAR FileName[1];
139
140 if (!DissectArcPath(Path, FileName, &DriveNumber, &DrivePartition))
141 return EINVAL;
142
143 if (DrivePartition == 0xff)
144 {
145 /* This is a CD-ROM device */
146 SectorSize = 2048;
147 }
148 else
149 {
150 /* This is either a floppy disk device (DrivePartition == 0) or
151 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF) but
152 * it doesn't matter which one because they both have 512 bytes per sector */
153 SectorSize = 512;
154 }
155
156 if (DrivePartition != 0xff && DrivePartition != 0)
157 {
158 if (!XboxDiskGetPartitionEntry(DriveNumber, DrivePartition, &PartitionTableEntry))
159 return EINVAL;
160 SectorOffset = PartitionTableEntry.SectorCountBeforePartition;
161 SectorCount = PartitionTableEntry.PartitionSectorCount;
162 }
163 else
164 {
165 SectorCount = 0; /* FIXME */
166 }
167
168 Context = MmHeapAlloc(sizeof(DISKCONTEXT));
169 if (!Context)
170 return ENOMEM;
171 Context->DriveNumber = DriveNumber;
172 Context->SectorSize = SectorSize;
173 Context->SectorOffset = SectorOffset;
174 Context->SectorCount = SectorCount;
175 Context->SectorNumber = 0;
176 FsSetDeviceSpecific(*FileId, Context);
177
178 return ESUCCESS;
179 }
180
181 static LONG DiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
182 {
183 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
184 UCHAR* Ptr = (UCHAR*)Buffer;
185 ULONG i, Length, Sectors;
186 BOOLEAN ret;
187
188 *Count = 0;
189 i = 0;
190 while (N > 0)
191 {
192 Length = N;
193 if (Length > DISKREADBUFFER_SIZE)
194 Length = DISKREADBUFFER_SIZE;
195 Sectors = (Length + Context->SectorSize - 1) / Context->SectorSize;
196 ret = MachDiskReadLogicalSectors(
197 Context->DriveNumber,
198 Context->SectorNumber + Context->SectorOffset + i,
199 Sectors,
200 (PVOID)DISKREADBUFFER);
201 if (!ret)
202 return EIO;
203 RtlCopyMemory(Ptr, (PVOID)DISKREADBUFFER, Length);
204 Ptr += Length;
205 *Count += Length;
206 N -= Length;
207 i += Sectors;
208 }
209
210 return ESUCCESS;
211 }
212
213 static LONG DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
214 {
215 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
216
217 if (SeekMode != SeekAbsolute)
218 return EINVAL;
219 if (Position->LowPart & (Context->SectorSize - 1))
220 return EINVAL;
221
222 /* FIXME: take HighPart into account */
223 Context->SectorNumber = Position->LowPart / Context->SectorSize;
224 return ESUCCESS;
225 }
226
227 static const DEVVTBL DiskVtbl = {
228 DiskClose,
229 DiskGetFileInformation,
230 DiskOpen,
231 DiskRead,
232 DiskSeek,
233 };
234
235 static VOID
236 GetHarddiskIdentifier(PCHAR Identifier,
237 UCHAR DriveNumber)
238 {
239 PMASTER_BOOT_RECORD Mbr;
240 ULONG *Buffer;
241 ULONG i;
242 ULONG Checksum;
243 ULONG Signature;
244 CHAR ArcName[256];
245 PARTITION_TABLE_ENTRY PartitionTableEntry;
246
247 /* Read the MBR */
248 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
249 {
250 ERR("Reading MBR failed\n");
251 return;
252 }
253
254 Buffer = (ULONG*)DISKREADBUFFER;
255 Mbr = (PMASTER_BOOT_RECORD)DISKREADBUFFER;
256
257 Signature = Mbr->Signature;
258 TRACE("Signature: %x\n", Signature);
259
260 /* Calculate the MBR checksum */
261 Checksum = 0;
262 for (i = 0; i < 128; i++)
263 {
264 Checksum += Buffer[i];
265 }
266 Checksum = ~Checksum + 1;
267 TRACE("Checksum: %x\n", Checksum);
268
269 /* Fill out the ARC disk block */
270 reactos_arc_disk_info[reactos_disk_count].Signature = Signature;
271 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
272 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)", reactos_disk_count);
273 strcpy(reactos_arc_strings[reactos_disk_count], ArcName);
274 reactos_arc_disk_info[reactos_disk_count].ArcName =
275 reactos_arc_strings[reactos_disk_count];
276 reactos_disk_count++;
277
278 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber - 0x80);
279 FsRegisterDevice(ArcName, &DiskVtbl);
280
281 /* Add partitions */
282 i = 1;
283 DiskReportError(FALSE);
284 while (XboxDiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
285 {
286 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
287 {
288 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber - 0x80, i);
289 FsRegisterDevice(ArcName, &DiskVtbl);
290 }
291 i++;
292 }
293 DiskReportError(TRUE);
294
295 /* Convert checksum and signature to identifier string */
296 Identifier[0] = Hex[(Checksum >> 28) & 0x0F];
297 Identifier[1] = Hex[(Checksum >> 24) & 0x0F];
298 Identifier[2] = Hex[(Checksum >> 20) & 0x0F];
299 Identifier[3] = Hex[(Checksum >> 16) & 0x0F];
300 Identifier[4] = Hex[(Checksum >> 12) & 0x0F];
301 Identifier[5] = Hex[(Checksum >> 8) & 0x0F];
302 Identifier[6] = Hex[(Checksum >> 4) & 0x0F];
303 Identifier[7] = Hex[Checksum & 0x0F];
304 Identifier[8] = '-';
305 Identifier[9] = Hex[(Signature >> 28) & 0x0F];
306 Identifier[10] = Hex[(Signature >> 24) & 0x0F];
307 Identifier[11] = Hex[(Signature >> 20) & 0x0F];
308 Identifier[12] = Hex[(Signature >> 16) & 0x0F];
309 Identifier[13] = Hex[(Signature >> 12) & 0x0F];
310 Identifier[14] = Hex[(Signature >> 8) & 0x0F];
311 Identifier[15] = Hex[(Signature >> 4) & 0x0F];
312 Identifier[16] = Hex[Signature & 0x0F];
313 Identifier[17] = '-';
314 Identifier[18] = 'A';
315 Identifier[19] = 0;
316 TRACE("Identifier: %s\n", Identifier);
317 }
318
319 static VOID
320 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
321 PCONFIGURATION_COMPONENT_DATA BusKey)
322 {
323 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
324 PCM_INT13_DRIVE_PARAMETER Int13Drives;
325 GEOMETRY Geometry;
326 PCONFIGURATION_COMPONENT_DATA DiskKey, ControllerKey;
327 UCHAR DiskCount;
328 USHORT i;
329 ULONG Size;
330 BOOLEAN Changed;
331
332 /* Count the number of visible drives */
333 DiskReportError(FALSE);
334 DiskCount = 0;
335
336 /* There are some really broken BIOSes out there. There are even BIOSes
337 * that happily report success when you ask them to read from non-existent
338 * harddisks. So, we set the buffer to known contents first, then try to
339 * read. If the BIOS reports success but the buffer contents haven't
340 * changed then we fail anyway */
341 memset((PVOID) DISKREADBUFFER, 0xcd, DISKREADBUFFER_SIZE);
342 while (MachDiskReadLogicalSectors(0x80 + DiskCount, 0ULL, 1, (PVOID)DISKREADBUFFER))
343 {
344 Changed = FALSE;
345 for (i = 0; ! Changed && i < DISKREADBUFFER_SIZE; i++)
346 {
347 Changed = ((PUCHAR)DISKREADBUFFER)[i] != 0xcd;
348 }
349 if (! Changed)
350 {
351 TRACE("BIOS reports success for disk %d but data didn't change\n",
352 (int)DiskCount);
353 break;
354 }
355 DiskCount++;
356 memset((PVOID) DISKREADBUFFER, 0xcd, DISKREADBUFFER_SIZE);
357 }
358 DiskReportError(TRUE);
359 TRACE("BIOS reports %d harddisk%s\n",
360 (int)DiskCount, (DiskCount == 1) ? "": "s");
361
362 //DetectBiosFloppyController(BusKey);
363
364 /* Allocate resource descriptor */
365 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
366 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
367 PartialResourceList = MmHeapAlloc(Size);
368 if (PartialResourceList == NULL)
369 {
370 ERR("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 = (USHORT)Geometry.Sectors;
394 Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
395 Int13Drives[i].NumberDrives = DiskCount;
396
397 TRACE(
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 TRACE("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 TRACE(
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 TRACE("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 TRACE("DetectHardware() Done\n");
512 return SystemKey;
513 }
514
515 VOID XboxHwIdle(VOID)
516 {
517 /* UNIMPLEMENTED */
518 }
519
520 /* EOF */