f8d628d264009884d76cbc74de0b8671b6806eef
[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 delay_count = 1;
116
117 do
118 {
119 /* Next delay count to try */
120 delay_count <<= 1;
121
122 WaitFor8254Wraparound();
123
124 /* Do the delay */
125 __StallExecutionProcessor(delay_count);
126
127 CurCount = Read8254Timer();
128 }
129 while (CurCount > LATCH / 2);
130
131 /* Get bottom value for delay */
132 delay_count >>= 1;
133
134 /* Stage 2: Fine calibration */
135
136 /* Which bit are we going to test */
137 calib_bit = delay_count;
138
139 for (i = 0; i < PRECISION; i++)
140 {
141 /* Next bit to calibrate */
142 calib_bit >>= 1;
143
144 /* If we have done all bits, stop */
145 if (!calib_bit) break;
146
147 /* Set the bit in delay_count */
148 delay_count |= calib_bit;
149
150 WaitFor8254Wraparound();
151
152 /* Do the delay */
153 __StallExecutionProcessor(delay_count);
154
155 CurCount = Read8254Timer();
156 /* If a tick has passed, turn the calibrated bit back off */
157 if (CurCount <= LATCH / 2)
158 delay_count &= ~calib_bit;
159 }
160
161 /* We're finished: Do the finishing touches */
162
163 /* Calculate delay_count for 1ms */
164 delay_count /= (MILLISEC / 2);
165 }
166
167
168 static
169 UCHAR
170 GetFloppyType(UCHAR DriveNumber)
171 {
172 UCHAR Data;
173
174 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x10);
175 Data = READ_PORT_UCHAR((PUCHAR)0x71);
176
177 if (DriveNumber == 0)
178 return Data >> 4;
179 else if (DriveNumber == 1)
180 return Data & 0x0F;
181
182 return 0;
183 }
184
185 static
186 PVOID
187 GetInt1eTable(VOID)
188 {
189 PUSHORT SegPtr = (PUSHORT)0x7A;
190 PUSHORT OfsPtr = (PUSHORT)0x78;
191
192 return (PVOID)((ULONG_PTR)(((ULONG)(*SegPtr)) << 4) + (ULONG)(*OfsPtr));
193 }
194
195 static
196 VOID
197 DetectBiosFloppyPeripheral(PCONFIGURATION_COMPONENT_DATA ControllerKey)
198 {
199 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
200 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
201 PCM_FLOPPY_DEVICE_DATA FloppyData;
202 CHAR Identifier[20];
203 PCONFIGURATION_COMPONENT_DATA PeripheralKey;
204 ULONG Size;
205 UCHAR FloppyNumber;
206 UCHAR FloppyType;
207 ULONG MaxDensity[6] = {0, 360, 1200, 720, 1440, 2880};
208 PUCHAR Ptr;
209
210 for (FloppyNumber = 0; FloppyNumber < 2; FloppyNumber++)
211 {
212 FloppyType = GetFloppyType(FloppyNumber);
213
214 if ((FloppyType > 5) || (FloppyType == 0))
215 continue;
216
217 if (!DiskResetController(FloppyNumber))
218 continue;
219
220 Ptr = GetInt1eTable();
221
222 /* Set 'Identifier' value */
223 sprintf(Identifier, "FLOPPY%d", FloppyNumber + 1);
224
225 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
226 sizeof(CM_FLOPPY_DEVICE_DATA);
227 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
228 if (PartialResourceList == NULL)
229 {
230 ERR("Failed to allocate resource descriptor\n");
231 return;
232 }
233
234 memset(PartialResourceList, 0, Size);
235 PartialResourceList->Version = 1;
236 PartialResourceList->Revision = 1;
237 PartialResourceList->Count = 1;
238
239 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
240 PartialDescriptor->Type = CmResourceTypeDeviceSpecific;
241 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
242 PartialDescriptor->u.DeviceSpecificData.DataSize = sizeof(CM_FLOPPY_DEVICE_DATA);
243
244 FloppyData = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
245 FloppyData->Version = 2;
246 FloppyData->Revision = 0;
247 FloppyData->MaxDensity = MaxDensity[FloppyType];
248 FloppyData->MountDensity = 0;
249 RtlCopyMemory(&FloppyData->StepRateHeadUnloadTime, Ptr, 11);
250 FloppyData->MaximumTrackValue = (FloppyType == 1) ? 39 : 79;
251 FloppyData->DataTransferRate = 0;
252
253 FldrCreateComponentKey(ControllerKey,
254 PeripheralClass,
255 FloppyDiskPeripheral,
256 Input | Output,
257 FloppyNumber,
258 0xFFFFFFFF,
259 Identifier,
260 PartialResourceList,
261 Size,
262 &PeripheralKey);
263 }
264 }
265
266 static
267 PCONFIGURATION_COMPONENT_DATA
268 DetectBiosFloppyController(PCONFIGURATION_COMPONENT_DATA BusKey)
269 {
270 PCONFIGURATION_COMPONENT_DATA ControllerKey;
271 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
272 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
273 ULONG Size;
274 ULONG FloppyCount;
275
276 FloppyCount = MachGetFloppyCount();
277 TRACE("Floppy count: %u\n", FloppyCount);
278
279 /* Always create a BIOS disk controller, no matter if we have floppy drives or not */
280 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
281 2 * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
282 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
283 if (PartialResourceList == NULL)
284 {
285 ERR("Failed to allocate resource descriptor\n");
286 return NULL;
287 }
288 memset(PartialResourceList, 0, Size);
289
290 /* Initialize resource descriptor */
291 PartialResourceList->Version = 1;
292 PartialResourceList->Revision = 1;
293 PartialResourceList->Count = 3;
294
295 /* Set IO Port */
296 PartialDescriptor = &PartialResourceList->PartialDescriptors[0];
297 PartialDescriptor->Type = CmResourceTypePort;
298 PartialDescriptor->ShareDisposition = CmResourceShareDeviceExclusive;
299 PartialDescriptor->Flags = CM_RESOURCE_PORT_IO;
300 PartialDescriptor->u.Port.Start.LowPart = 0x03F0;
301 PartialDescriptor->u.Port.Start.HighPart = 0x0;
302 PartialDescriptor->u.Port.Length = 8;
303
304 /* Set Interrupt */
305 PartialDescriptor = &PartialResourceList->PartialDescriptors[1];
306 PartialDescriptor->Type = CmResourceTypeInterrupt;
307 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
308 PartialDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
309 PartialDescriptor->u.Interrupt.Level = 6;
310 PartialDescriptor->u.Interrupt.Vector = 6;
311 PartialDescriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
312
313 /* Set DMA channel */
314 PartialDescriptor = &PartialResourceList->PartialDescriptors[2];
315 PartialDescriptor->Type = CmResourceTypeDma;
316 PartialDescriptor->ShareDisposition = CmResourceShareUndetermined;
317 PartialDescriptor->Flags = 0;
318 PartialDescriptor->u.Dma.Channel = 2;
319 PartialDescriptor->u.Dma.Port = 0;
320
321 /* Create floppy disk controller */
322 FldrCreateComponentKey(BusKey,
323 ControllerClass,
324 DiskController,
325 Output | Input,
326 0x0,
327 0xFFFFFFFF,
328 NULL,
329 PartialResourceList,
330 Size,
331 &ControllerKey);
332 TRACE("Created key: DiskController\\0\n");
333
334 if (FloppyCount)
335 DetectBiosFloppyPeripheral(ControllerKey);
336
337 return ControllerKey;
338 }
339
340 VOID
341 DetectBiosDisks(PCONFIGURATION_COMPONENT_DATA SystemKey,
342 PCONFIGURATION_COMPONENT_DATA BusKey)
343 {
344 PCONFIGURATION_COMPONENT_DATA ControllerKey, DiskKey;
345 PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
346 PCM_INT13_DRIVE_PARAMETER Int13Drives;
347 GEOMETRY Geometry;
348 UCHAR DiskCount, DriveNumber;
349 USHORT i;
350 ULONG Size;
351
352 /* The pre-enumeration of the BIOS disks was already done in InitializeBootDevices() */
353 DiskCount = PcBiosDiskCount;
354
355 /* Use the floppy disk controller as our controller */
356 ControllerKey = DetectBiosFloppyController(BusKey);
357 if (!ControllerKey)
358 {
359 ERR("Failed to detect BIOS disk controller\n");
360 return;
361 }
362
363 /* Allocate resource descriptor */
364 Size = sizeof(CM_PARTIAL_RESOURCE_LIST) +
365 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
366 PartialResourceList = FrLdrHeapAlloc(Size, TAG_HW_RESOURCE_LIST);
367 if (PartialResourceList == NULL)
368 {
369 ERR("Failed to allocate resource descriptor\n");
370 return;
371 }
372
373 /* Initialize resource descriptor */
374 memset(PartialResourceList, 0, Size);
375 PartialResourceList->Version = 1;
376 PartialResourceList->Revision = 1;
377 PartialResourceList->Count = 1;
378 PartialResourceList->PartialDescriptors[0].Type = CmResourceTypeDeviceSpecific;
379 PartialResourceList->PartialDescriptors[0].ShareDisposition = 0;
380 PartialResourceList->PartialDescriptors[0].Flags = 0;
381 PartialResourceList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
382 sizeof(CM_INT13_DRIVE_PARAMETER) * DiskCount;
383
384 /* Get harddisk Int13 geometry data */
385 Int13Drives = (PVOID)(((ULONG_PTR)PartialResourceList) + sizeof(CM_PARTIAL_RESOURCE_LIST));
386 for (i = 0; i < DiskCount; i++)
387 {
388 DriveNumber = 0x80 + i;
389
390 if (MachDiskGetDriveGeometry(DriveNumber, &Geometry))
391 {
392 Int13Drives[i].DriveSelect = DriveNumber;
393 Int13Drives[i].MaxCylinders = Geometry.Cylinders - 1;
394 Int13Drives[i].SectorsPerTrack = (USHORT)Geometry.Sectors;
395 Int13Drives[i].MaxHeads = (USHORT)Geometry.Heads - 1;
396 Int13Drives[i].NumberDrives = DiskCount;
397
398 TRACE("Disk %x: %u Cylinders %u Heads %u Sectors %u Bytes\n",
399 DriveNumber,
400 Geometry.Cylinders - 1,
401 Geometry.Heads - 1,
402 Geometry.Sectors,
403 Geometry.BytesPerSector);
404 }
405 }
406
407 /* Update the 'System' key's configuration data with BIOS INT13h information */
408 FldrSetConfigurationData(SystemKey, PartialResourceList, Size);
409
410 /* Create and fill subkey for each harddisk */
411 for (i = 0; i < DiskCount; i++)
412 {
413 PCHAR Identifier;
414
415 DriveNumber = 0x80 + i;
416
417 /* Get disk values */
418 PartialResourceList = GetHarddiskConfigurationData(DriveNumber, &Size);
419 Identifier = GetHarddiskIdentifier(DriveNumber);
420
421 /* Create disk key */
422 FldrCreateComponentKey(ControllerKey,
423 PeripheralClass,
424 DiskPeripheral,
425 Output | Input,
426 0x0,
427 0xFFFFFFFF,
428 Identifier,
429 PartialResourceList,
430 Size,
431 &DiskKey);
432 }
433 }
434
435 VOID
436 FrLdrCheckCpuCompatibility(VOID)
437 {
438 INT CpuInformation[4] = {-1};
439 ULONG NumberOfIds;
440
441 /* Check if the processor first supports ID 1 */
442 __cpuid(CpuInformation, 0);
443
444 NumberOfIds = CpuInformation[0];
445
446 if (NumberOfIds == 0)
447 {
448 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
449 __FILE__,
450 __LINE__,
451 "ReactOS requires the CPUID instruction to return "
452 "more than one supported ID.\n\n");
453 }
454
455 /* NumberOfIds will be greater than 1 if the processor is new enough */
456 if (NumberOfIds == 1)
457 {
458 INT ProcessorFamily;
459
460 /* Get information */
461 __cpuid(CpuInformation, 1);
462
463 ProcessorFamily = (CpuInformation[0] >> 8) & 0xF;
464
465 /* If it's Family 4 or lower, bugcheck */
466 if (ProcessorFamily < 5)
467 {
468 FrLdrBugCheckWithMessage(MISSING_HARDWARE_REQUIREMENTS,
469 __FILE__,
470 __LINE__,
471 "Processor is too old (family %u < 5)\n"
472 "ReactOS requires a Pentium-level processor or newer.",
473 ProcessorFamily);
474 }
475 }
476 }
477
478 /* EOF */