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