80bf3983dd66ca4fbf9fed9d9536bd0ba478a006
[reactos.git] / reactos / boot / freeldr / freeldr / arch / i386 / hardware.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 #define NDEBUG
25 #include <debug.h>
26
27 DBG_DEFAULT_CHANNEL(HWDETECT);
28
29
30 #define MILLISEC (10)
31 #define PRECISION (8)
32
33 #define HZ (100)
34 #define CLOCK_TICK_RATE (1193182)
35 #define LATCH (CLOCK_TICK_RATE / HZ)
36
37 static unsigned int delay_count = 1;
38 extern UCHAR PcBiosDiskCount;
39
40 /* This function is slightly different in its PC and XBOX versions */
41 GET_HARDDISK_CONFIG_DATA GetHarddiskConfigurationData = NULL;
42
43 PCHAR
44 GetHarddiskIdentifier(UCHAR DriveNumber);
45
46 /* FUNCTIONS *****************************************************************/
47
48 static
49 VOID
50 __StallExecutionProcessor(ULONG Loops)
51 {
52 register volatile unsigned int i;
53 for (i = 0; i < Loops; i++);
54 }
55
56 VOID StallExecutionProcessor(ULONG Microseconds)
57 {
58 ULONGLONG LoopCount = ((ULONGLONG)delay_count * (ULONGLONG)Microseconds) / 1000ULL;
59 __StallExecutionProcessor((ULONG)LoopCount);
60 }
61
62 static
63 ULONG
64 Read8254Timer(VOID)
65 {
66 ULONG Count;
67
68 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
69 Count = READ_PORT_UCHAR((PUCHAR)0x40);
70 Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
71
72 return Count;
73 }
74
75 static
76 VOID
77 WaitFor8254Wraparound(VOID)
78 {
79 ULONG CurCount;
80 ULONG PrevCount = ~0;
81 LONG Delta;
82
83 CurCount = Read8254Timer();
84
85 do
86 {
87 PrevCount = CurCount;
88 CurCount = Read8254Timer();
89 Delta = CurCount - PrevCount;
90
91 /*
92 * This limit for delta seems arbitrary, but it isn't, it's
93 * slightly above the level of error a buggy Mercury/Neptune
94 * chipset timer can cause.
95 */
96 }
97 while (Delta < 300);
98 }
99
100 VOID
101 HalpCalibrateStallExecution(VOID)
102 {
103 ULONG i;
104 ULONG calib_bit;
105 ULONG CurCount;
106
107 /* Initialise timer interrupt with MILLISECOND ms interval */
108 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
109 WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH & 0xff); /* LSB */
110 WRITE_PORT_UCHAR((PUCHAR)0x40, LATCH >> 8); /* MSB */
111
112 /* Stage 1: Coarse calibration */
113
114 WaitFor8254Wraparound();
115
116 delay_count = 1;
117
118 do
119 {
120 /* Next delay count to try */
121 delay_count <<= 1;
122
123 WaitFor8254Wraparound();
124
125 /* Do the delay */
126 __StallExecutionProcessor(delay_count);
127
128 CurCount = Read8254Timer();
129 }
130 while (CurCount > LATCH / 2);
131
132 /* Get bottom value for delay */
133 delay_count >>= 1;
134
135 /* Stage 2: Fine calibration */
136
137 /* Which bit are we going to test */
138 calib_bit = delay_count;
139
140 for (i = 0; i < PRECISION; i++)
141 {
142 /* Next bit to calibrate */
143 calib_bit >>= 1;
144
145 /* If we have done all bits, stop */
146 if (!calib_bit) break;
147
148 /* Set the bit in delay_count */
149 delay_count |= calib_bit;
150
151 WaitFor8254Wraparound();
152
153 /* Do the delay */
154 __StallExecutionProcessor(delay_count);
155
156 CurCount = Read8254Timer();
157 /* If a tick has passed, turn the calibrated bit back off */
158 if (CurCount <= LATCH / 2)
159 delay_count &= ~calib_bit;
160 }
161
162 /* We're finished: Do the finishing touches */
163
164 /* Calculate delay_count for 1ms */
165 delay_count /= (MILLISEC / 2);
166 }
167
168
169 static
170 UCHAR
171 GetFloppyCount(VOID)
172 {
173 UCHAR Data;
174
175 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x10);
176 Data = READ_PORT_UCHAR((PUCHAR)0x71);
177
178 return ((Data & 0xF0) ? 1 : 0) + ((Data & 0x0F) ? 1 : 0);
179 }
180
181 static
182 UCHAR
183 GetFloppyType(UCHAR DriveNumber)
184 {
185 UCHAR Data;
186
187 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x10);
188 Data = READ_PORT_UCHAR((PUCHAR)0x71);
189
190 if (DriveNumber == 0)
191 return Data >> 4;
192 else if (DriveNumber == 1)
193 return Data & 0x0F;
194
195 return 0;
196 }
197
198 static
199 PVOID
200 GetInt1eTable(VOID)
201 {
202 PUSHORT SegPtr = (PUSHORT)0x7A;
203 PUSHORT OfsPtr = (PUSHORT)0x78;
204
205 return (PVOID)((ULONG_PTR)(((ULONG)(*SegPtr)) << 4) + (ULONG)(*OfsPtr));
206 }
207
208 static
209 VOID
210 DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
211 {
212 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
213 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
214 PCM_FLOPPY_DEVICE_DATA FloppyData;
215 CHAR Identifier[20];
216 PCONFIGURATION_COMPONENT_DATA PeripheralKey;
217 ULONG Size;
218 UCHAR FloppyNumber;
219 UCHAR FloppyType;
220 ULONG MaxDensity[6] = {0, 360, 1200, 720, 1440, 2880};
221 PUCHAR Ptr;
222
223 for (FloppyNumber = 0; FloppyNumber < 2; FloppyNumber++)
224 {
225 FloppyType = GetFloppyType(FloppyNumber);
226
227 if ((FloppyType > 5) || (FloppyType == 0))
228 continue;
229
230 if (!DiskResetController(FloppyNumber))
231 continue;
232
233 Ptr = GetInt1eTable();
234
235 /* Set 'Identifier' value */
236 sprintf(Identifier, "FLOPPY%d", FloppyNumber + 1);
237
238 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
239 sizeof(CM_FLOPPY_DEVICE_DATA);
240 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
241 if (PartialResourceList == NULL)
242 {
243 ERR("Failed to allocate resource descriptor\n");
244 return;
245 }
246
247 memset(PartialResourceList, 0, Size);
248 PartialResourceList->Version = 1;
249 PartialResourceList->Revision = 1;
250 PartialResourceList->Count = 1;
251
252 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
253 PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
254 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
255 PartialDescriptor->u.DeviceSpecificData.DataSize = sizeof(CM_FLOPPY_DEVICE_DATA);
256
257 FloppyData = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
258 FloppyData->Version = 2;
259 FloppyData->Revision = 0;
260 FloppyData->MaxDensity = MaxDensity[FloppyType];
261 FloppyData->MountDensity = 0;
262 RtlCopyMemory(&FloppyData->StepRateHeadUnloadTime, Ptr, 11);
263 FloppyData->MaximumTrackValue = (FloppyType == 1) ? 39 : 79;
264 FloppyData->DataTransferRate = 0;
265
266 FldrCreateComponentKey(ControllerKey,
267 PeripheralClass,
268 FloppyDiskPeripheral,
269 Input | Output,
270 FloppyNumber,
271 0xFFFFFFFF,
272 Identifier,
273 PartialResourceList,
274 Size,
275 &PeripheralKey);
276 }
277 }
278
279 static
280 PCONFIGURATION_COMPONENT_DATA
281 DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey)
282 {
283 PCONFIGURATION_COMPONENT_DATA ControllerKey;
284 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
285 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
286 ULONG Size;
287 ULONG FloppyCount;
288
289 FloppyCount = GetFloppyCount();
290 TRACE("Floppy count: %u\n", FloppyCount);
291
292 /* Always create a BIOS disk controller, no matter if we have floppy drives or not */
293 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
294 2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
295 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
296 if (PartialResourceList == NULL)
297 {
298 ERR("Failed to allocate resource descriptor\n");
299 return NULL;
300 }
301 memset(PartialResourceList, 0, Size);
302
303 /* Initialize resource descriptor */
304 PartialResourceList->Version = 1;
305 PartialResourceList->Revision = 1;
306 PartialResourceList->Count = 3;
307
308 /* Set IO Port */
309 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
310 PartialDescriptor->Type = CmResourceTypePort;
311 PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
312 PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
313 PartialDescriptor->u.Port.Start.LowPart = 0x03F0;
314 PartialDescriptor->u.Port.Start.HighPart = 0x0;
315 PartialDescriptor->u.Port.Length = 8;
316
317 /* Set Interrupt */
318 PartialDescriptor = &PartialResourceList->PartialDescriptors[1];
319 PartialDescriptor->Type = CmResourceTypeInterrupt;
320 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
321 PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
322 PartialDescriptor->u.Interrupt.Level = 6;
323 PartialDescriptor->u.Interrupt.Vector = 6;
324 PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
325
326 /* Set DMA channel */
327 PartialDescriptor = &PartialResourceList->PartialDescriptors[2];
328 PartialDescriptor->Type = CmResourceTypeDma;
329 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
330 PartialDescriptor->Flags = 0;
331 PartialDescriptor->u.Dma.Channel = 2;
332 PartialDescriptor->u.Dma.Port = 0;
333
334 /* Create floppy disk controller */
335 FldrCreateComponentKey(BusKey,
336 ControllerClass,
337 DiskController,
338 Output | Input,
339 0x0,
340 0xFFFFFFFF,
341 NULL,
342 PartialResourceList,
343 Size,
344 &ControllerKey);
345 TRACE("Created key: DiskController\\0\n");
346
347 if (FloppyCount)
348 DetectBiosFloppyPeripheral(ControllerKey);
349
350 return ControllerKey;
351 }
352
353 VOID
354 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
355 PCONFIGURATION_COMPONENT_DATA BusKey)
356 {
357 PCONFIGURATION_COMPONENT_DATA ControllerKey, DiskKey;
358 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
359 PCM_INT13_DRIVE_PARAMETER Int13Drives;
360 GEOMETRY Geometry;
361 UCHAR DiskCount, DriveNumber;
362 USHORT i;
363 ULONG Size;
364
365 /* The pre-enumeration of the BIOS disks was already done in InitializeBootDevices() */
366 DiskCount = PcBiosDiskCount;
367
368 /* Use the floppy disk controller as our controller */
369 ControllerKey = DetectBiosFloppyController(BusKey);
370 if (!ControllerKey)
371 {
372 ERR("Failed to detect BIOS disk controller\n");
373 return;
374 }
375
376 /* Allocate resource descriptor */
377 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
378 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
379 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
380 if (PartialResourceList == NULL)
381 {
382 ERR("Failed to allocate resource descriptor\n");
383 return;
384 }
385
386 /* Initialize resource descriptor */
387 memset(PartialResourceList, 0, Size);
388 PartialResourceList->Version = 1;
389 PartialResourceList->Revision = 1;
390 PartialResourceList->Count = 1;
391 PartialResourceList->PartialDescriptors[0].Type = CmResourceTypeDeviceSpecific;
392 PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
393 PartialResourceList->PartialDescriptors[0].Flags = 0;
394 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
395 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
396
397 /* Get harddisk Int13 geometry data */
398 Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
399 for (i = 0; i < DiskCount; i++)
400 {
401 DriveNumber = 0x80 + i;
402
403 if (MachDiskGetDriveGeometry(DriveNumber, &Geometry))
404 {
405 Int13Drives[i].DriveSelect = DriveNumber;
406 Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
407 Int13Drives[i].SectorsPerTrack = (USHORT)Geometry.Sectors;
408 Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
409 Int13Drives[i].NumberDrives = DiskCount;
410
411 TRACE("Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
412 DriveNumber,
413 Geometry.Cylinders - 1,
414 Geometry.Heads - 1,
415 Geometry.Sectors,
416 Geometry.BytesPerSector);
417 }
418 }
419
420 /* Update the 'System' key's configuration data with BIOS INT13h information */
421 FldrSetConfigurationData(SystemKey, PartialResourceList, Size);
422
423 /* Create and fill subkey for each harddisk */
424 for (i = 0; i < DiskCount; i++)
425 {
426 PCHAR Identifier;
427
428 DriveNumber = 0x80 + i;
429
430 /* Get disk values */
431 PartialResourceList = GetHarddiskConfigurationData(DriveNumber, &Size);
432 Identifier = GetHarddiskIdentifier(DriveNumber);
433
434 /* Create disk key */
435 FldrCreateComponentKey(ControllerKey,
436 PeripheralClass,
437 DiskPeripheral,
438 Output | Input,
439 0x0,
440 0xFFFFFFFF,
441 Identifier,
442 PartialResourceList,
443 Size,
444 &DiskKey);
445 }
446 }
447
448 VOID
449 FrLdrCheckCpuCompatibility(VOID)
450 {
451 INT CpuInformation[4] = {-1};
452 ULONG NumberOfIds;
453
454 /* Check if the processor first supports ID 1 */
455 __cpuid(CpuInformation, 0);
456
457 NumberOfIds = CpuInformation[0];
458
459 if (NumberOfIds == 0)
460 {
461 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
462 __FILE__,
463 __LINE__,
464 "ReactOS requires the CPUID instruction to return "
465 "more than one supported ID.\n\n");
466 }
467
468 /* NumberOfIds will be greater than 1 if the processor is new enough */
469 if (NumberOfIds == 1)
470 {
471 INT ProcessorFamily;
472
473 /* Get information */
474 __cpuid(CpuInformation, 1);
475
476 ProcessorFamily = (CpuInformation[0] >> 8) & 0xF;
477
478 /* If it's Family 4 or lower, bugcheck */
479 if (ProcessorFamily < 5)
480 {
481 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
482 __FILE__,
483 __LINE__,
484 "Processor is too old (family %u < 5)\n"
485 "ReactOS requires a Pentium-level processor or newer.",
486 ProcessorFamily);
487 }
488 }
489 }
490
491 /* EOF */