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