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