6a033f36186c40f20b34b20832b3262446152674
[reactos.git] / reactos / boot / freeldr / freeldr / arch / i386 / hwcpu.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2003 Eric Kohl
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <freeldr.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 #define MP_FP_SIGNATURE 0x5F504D5F /* "_MP_" */
27 #define MP_CT_SIGNATURE 0x504D4350 /* "PCMP" */
28
29
30 typedef struct _MP_FLOATING_POINT_TABLE
31 {
32 ULONG Signature; /* "_MP_" */
33 ULONG PhysicalAddressPointer;
34 UCHAR Length;
35 UCHAR SpecRev;
36 UCHAR Checksum;
37 UCHAR FeatureByte[5];
38 } PACKED MP_FLOATING_POINT_TABLE, *PMP_FLOATING_POINT_TABLE;
39
40
41 typedef struct _MPS_CONFIG_TABLE_HEADER
42 {
43 ULONG Signature; /* "PCMP" */
44 USHORT BaseTableLength;
45 UCHAR SpecRev;
46 UCHAR Checksum;
47 UCHAR OemIdString[8];
48 UCHAR ProductIdString[12];
49 ULONG OemTablePointer;
50 USHORT OemTableLength;
51 USHORT EntryCount;
52 ULONG AddressOfLocalAPIC;
53 USHORT ExtendedTableLength;
54 UCHAR ExtendedTableChecksum;
55 UCHAR Reserved;
56 } PACKED MP_CONFIGURATION_TABLE, *PMP_CONFIGURATION_TABLE;
57
58
59 typedef struct _MP_PROCESSOR_ENTRY
60 {
61 UCHAR EntryType;
62 UCHAR LocalApicId;
63 UCHAR LocalApicVersion;
64 UCHAR CpuFlags;
65 ULONG CpuSignature;
66 ULONG FeatureFlags;
67 ULONG Reserved1;
68 ULONG Reserved2;
69 } PACKED MP_PROCESSOR_ENTRY, *PMP_PROCESSOR_ENTRY;
70
71
72 /* FUNCTIONS ****************************************************************/
73
74 static ULONG
75 GetCpuSpeed(VOID)
76 {
77 ULONGLONG Timestamp1;
78 ULONGLONG Timestamp2;
79 ULONGLONG Diff;
80
81 /* Read TSC (Time Stamp Counter) */
82 Timestamp1 = RDTSC();
83
84 /* Wait for 0.1 seconds (= 100 milliseconds = 100000 microseconds)*/
85 StallExecutionProcessor(100000);
86
87 /* Read TSC (Time Stamp Counter) again */
88 Timestamp2 = RDTSC();
89
90 /* Calculate elapsed time (check for counter overrun) */
91 if (Timestamp2 > Timestamp1)
92 {
93 Diff = Timestamp2 - Timestamp1;
94 }
95 else
96 {
97 Diff = Timestamp2 + (((ULONGLONG)-1) - Timestamp1);
98 }
99
100 return (ULONG)(Diff / 100000);
101 }
102
103
104 static VOID
105 DetectCPU(FRLDRHKEY CpuKey,
106 FRLDRHKEY FpuKey)
107 {
108 WCHAR VendorIdentifier[13];
109 CHAR tmpVendorIdentifier[13];
110 WCHAR Identifier[64];
111 ULONG FeatureSet;
112 FRLDRHKEY CpuInstKey;
113 FRLDRHKEY FpuInstKey;
114 ULONG eax = 0;
115 ULONG ebx = 0;
116 ULONG ecx = 0;
117 ULONG edx = 0;
118 ULONG *Ptr;
119 LONG Error;
120 BOOL SupportTSC = FALSE;
121 ULONG CpuSpeed;
122
123
124 /* Create the CPU instance key */
125 Error = RegCreateKey(CpuKey,
126 L"0",
127 &CpuInstKey);
128 if (Error != ERROR_SUCCESS)
129 {
130 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
131 return;
132 }
133
134 /* Create the FPU instance key */
135 Error = RegCreateKey(FpuKey,
136 L"0",
137 &FpuInstKey);
138 if (Error != ERROR_SUCCESS)
139 {
140 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
141 return;
142 }
143
144 eax = CpuidSupported();
145 if (eax & 1)
146 {
147 DbgPrint((DPRINT_HWDETECT, "CPUID supported\n"));
148
149 /* Get vendor identifier */
150 GetCpuid(0, &eax, &ebx, &ecx, &edx);
151 tmpVendorIdentifier[12] = 0;
152 Ptr = (ULONG*)&tmpVendorIdentifier[0];
153 *Ptr = ebx;
154 Ptr++;
155 *Ptr = edx;
156 Ptr++;
157 *Ptr = ecx;
158 swprintf(VendorIdentifier, L"%s", tmpVendorIdentifier);
159
160 /* Get Identifier */
161 GetCpuid(1, &eax, &ebx, &ecx, &edx);
162 swprintf(Identifier,
163 L"x86 Family %u Model %u Stepping %u",
164 (unsigned int)((eax >> 8) & 0x0F),
165 (unsigned int)((eax >> 4) & 0x0F),
166 (unsigned int)(eax & 0x0F));
167 FeatureSet = edx;
168 if (((eax >> 8) & 0x0F) >= 5)
169 SupportTSC = TRUE;
170 }
171 else
172 {
173 DbgPrint((DPRINT_HWDETECT, "CPUID not supported\n"));
174
175 wcscpy(VendorIdentifier, L"Unknown");
176 swprintf(Identifier,
177 L"x86 Family %u Model %u Stepping %u",
178 (unsigned int)((eax >> 8) & 0x0F),
179 (unsigned int)((eax >> 4) & 0x0F),
180 (unsigned int)(eax & 0x0F));
181 FeatureSet = 0;
182 }
183
184 /* Set 'Conmponent Information' value (CPU and FPU) */
185 SetComponentInformation(CpuInstKey, 0, 0, 1);
186 SetComponentInformation(FpuInstKey, 0, 0, 1);
187
188 /* Set 'FeatureSet' value (CPU only) */
189 DbgPrint((DPRINT_HWDETECT, "FeatureSet: %x\n", FeatureSet));
190
191 Error = RegSetValue(CpuInstKey,
192 L"FeatureSet",
193 REG_DWORD,
194 (PCHAR)&FeatureSet,
195 sizeof(ULONG));
196 if (Error != ERROR_SUCCESS)
197 {
198 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
199 }
200
201 /* Set 'Identifier' value (CPU and FPU) */
202 DbgPrint((DPRINT_HWDETECT, "Identifier: %S\n", Identifier));
203
204 Error = RegSetValue(CpuInstKey,
205 L"Identifier",
206 REG_SZ,
207 (PCHAR)Identifier,
208 (wcslen(Identifier) + 1)* sizeof(WCHAR));
209 if (Error != ERROR_SUCCESS)
210 {
211 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
212 }
213
214 Error = RegSetValue(FpuInstKey,
215 L"Identifier",
216 REG_SZ,
217 (PCHAR)Identifier,
218 (wcslen(Identifier) + 1) * sizeof(WCHAR));
219 if (Error != ERROR_SUCCESS)
220 {
221 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
222 }
223
224 /* Set 'VendorIdentifier' value (CPU only) */
225 DbgPrint((DPRINT_HWDETECT, "Vendor Identifier: %S\n", VendorIdentifier));
226
227 Error = RegSetValue(CpuInstKey,
228 L"VendorIdentifier",
229 REG_SZ,
230 (PCHAR)VendorIdentifier,
231 (wcslen(VendorIdentifier) + 1) * sizeof(WCHAR));
232 if (Error != ERROR_SUCCESS)
233 {
234 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
235 }
236
237 /* FIXME: Set 'Update Signature' value (CPU only) */
238
239 /* FIXME: Set 'Update Status' value (CPU only) */
240
241 /* Set '~MHz' value (CPU only) */
242 if (SupportTSC)
243 {
244 CpuSpeed = GetCpuSpeed();
245
246 Error = RegSetValue(CpuInstKey,
247 L"~MHz",
248 REG_DWORD,
249 (PCHAR)&CpuSpeed,
250 sizeof(ULONG));
251 if (Error != ERROR_SUCCESS)
252 {
253 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
254 }
255 }
256 }
257
258
259 static VOID
260 SetMpsProcessor(FRLDRHKEY CpuKey,
261 FRLDRHKEY FpuKey,
262 PMP_PROCESSOR_ENTRY CpuEntry)
263 {
264 WCHAR VendorIdentifier[13];
265 CHAR tmpVendorIdentifier[13];
266 WCHAR Identifier[64];
267 WCHAR Buffer[8];
268 ULONG FeatureSet;
269 FRLDRHKEY CpuInstKey;
270 FRLDRHKEY FpuInstKey;
271 ULONG eax = 0;
272 ULONG ebx = 0;
273 ULONG ecx = 0;
274 ULONG edx = 0;
275 ULONG *Ptr;
276 LONG Error;
277 ULONG CpuSpeed;
278
279 /* Get processor instance number */
280 swprintf(Buffer, L"%u", CpuEntry->LocalApicId);
281
282 /* Create the CPU instance key */
283 Error = RegCreateKey(CpuKey,
284 Buffer,
285 &CpuInstKey);
286 if (Error != ERROR_SUCCESS)
287 {
288 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
289 return;
290 }
291
292 /* Create the FPU instance key */
293 Error = RegCreateKey(FpuKey,
294 Buffer,
295 &FpuInstKey);
296 if (Error != ERROR_SUCCESS)
297 {
298 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
299 return;
300 }
301
302 /* Get 'VendorIdentifier' */
303 GetCpuid(0, &eax, &ebx, &ecx, &edx);
304 tmpVendorIdentifier[12] = 0;
305 Ptr = (ULONG*)&tmpVendorIdentifier[0];
306 *Ptr = ebx;
307 Ptr++;
308 *Ptr = edx;
309 Ptr++;
310 *Ptr = ecx;
311 swprintf(VendorIdentifier, L"%s", tmpVendorIdentifier);
312
313 /* Get 'Identifier' */
314 swprintf(Identifier,
315 L"x86 Family %u Model %u Stepping %u",
316 (ULONG)((CpuEntry->CpuSignature >> 8) & 0x0F),
317 (ULONG)((CpuEntry->CpuSignature >> 4) & 0x0F),
318 (ULONG)(CpuEntry->CpuSignature & 0x0F));
319
320 /* Get FeatureSet */
321 FeatureSet = CpuEntry->FeatureFlags;
322
323 /* Set 'Configuration Data' value (CPU and FPU) */
324 SetComponentInformation(CpuInstKey,
325 0,
326 CpuEntry->LocalApicId,
327 1 << CpuEntry->LocalApicId);
328
329 SetComponentInformation(FpuInstKey,
330 0,
331 CpuEntry->LocalApicId,
332 1 << CpuEntry->LocalApicId);
333
334 /* Set 'FeatureSet' value (CPU only) */
335 DbgPrint((DPRINT_HWDETECT, "FeatureSet: %x\n", FeatureSet));
336
337 Error = RegSetValue(CpuInstKey,
338 L"FeatureSet",
339 REG_DWORD,
340 (PCHAR)&FeatureSet,
341 sizeof(ULONG));
342 if (Error != ERROR_SUCCESS)
343 {
344 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
345 }
346
347 /* Set 'Identifier' value (CPU and FPU) */
348 DbgPrint((DPRINT_HWDETECT, "Identifier: %S\n", Identifier));
349
350 Error = RegSetValue(CpuInstKey,
351 L"Identifier",
352 REG_SZ,
353 (PCHAR)Identifier,
354 (wcslen(Identifier) + 1) * sizeof(WCHAR));
355 if (Error != ERROR_SUCCESS)
356 {
357 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
358 }
359
360 Error = RegSetValue(FpuInstKey,
361 L"Identifier",
362 REG_SZ,
363 (PCHAR)Identifier,
364 (wcslen(Identifier) + 1) * sizeof(WCHAR));
365 if (Error != ERROR_SUCCESS)
366 {
367 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
368 }
369
370 /* Set 'VendorIdentifier' value (CPU only) */
371 DbgPrint((DPRINT_HWDETECT, "Vendor Identifier: %S\n", VendorIdentifier));
372
373 Error = RegSetValue(CpuInstKey,
374 L"VendorIdentifier",
375 REG_SZ,
376 (PCHAR)VendorIdentifier,
377 (wcslen(VendorIdentifier) + 1) * sizeof(WCHAR));
378 if (Error != ERROR_SUCCESS)
379 {
380 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
381 }
382
383 /* FIXME: Set 'Update Signature' value (CPU only) */
384
385 /* FIXME: Set 'Update Status' value (CPU only) */
386
387 /* Set '~MHz' value (CPU only) */
388 if (((CpuEntry->CpuSignature >> 8) & 0x0F) >= 5)
389 {
390 CpuSpeed = GetCpuSpeed();
391
392 Error = RegSetValue(CpuInstKey,
393 L"~MHz",
394 REG_DWORD,
395 (PCHAR)&CpuSpeed,
396 sizeof(ULONG));
397 if (Error != ERROR_SUCCESS)
398 {
399 DbgPrint((DPRINT_HWDETECT, "RegSetValue() failed (Error %u)\n", (int)Error));
400 }
401 }
402 }
403
404
405 static PMP_FLOATING_POINT_TABLE
406 GetMpFloatingPointTable(VOID)
407 {
408 PMP_FLOATING_POINT_TABLE FpTable;
409 char *Ptr;
410 UCHAR Sum;
411 ULONG Length;
412 ULONG i;
413
414 FpTable = (PMP_FLOATING_POINT_TABLE)0xF0000;
415 while ((ULONG)FpTable < 0x100000)
416 {
417 if (FpTable->Signature == MP_FP_SIGNATURE)
418 {
419 Length = FpTable->Length * 0x10;
420 Ptr = (char *)FpTable;
421 Sum = 0;
422 for (i = 0; i < Length; i++)
423 {
424 Sum += Ptr[i];
425 }
426 DbgPrint((DPRINT_HWDETECT,
427 "Checksum: %u\n",
428 Sum));
429
430 if (Sum != 0)
431 {
432 DbgPrint((DPRINT_HWDETECT,
433 "Invalid MP floating point checksum: %u\n",
434 Sum));
435 return NULL;
436 }
437
438 return FpTable;
439 }
440
441 FpTable = (PMP_FLOATING_POINT_TABLE)((ULONG)FpTable + 0x10);
442 }
443
444 return NULL;
445 }
446
447
448 static PMP_CONFIGURATION_TABLE
449 GetMpConfigurationTable(PMP_FLOATING_POINT_TABLE FpTable)
450 {
451 PMP_CONFIGURATION_TABLE ConfigTable;
452 char *Ptr;
453 UCHAR Sum;
454 ULONG Length;
455 ULONG i;
456
457 if (FpTable->FeatureByte[0] != 0 ||
458 FpTable->PhysicalAddressPointer == 0)
459 return NULL;
460
461 ConfigTable = (PMP_CONFIGURATION_TABLE)FpTable->PhysicalAddressPointer;
462 if (ConfigTable->Signature != MP_CT_SIGNATURE)
463 return NULL;
464
465 DbgPrint((DPRINT_HWDETECT,
466 "MP Configuration Table at: %x\n",
467 (ULONG)ConfigTable));
468
469 /* Calculate base table checksum */
470 Length = ConfigTable->BaseTableLength;
471 Ptr = (char *)ConfigTable;
472 Sum = 0;
473 for (i = 0; i < Length; i++)
474 {
475 Sum += Ptr[i];
476 }
477 DbgPrint((DPRINT_HWDETECT,
478 "MP Configuration Table base checksum: %u\n",
479 Sum));
480
481 if (Sum != 0)
482 {
483 DbgPrint((DPRINT_HWDETECT,
484 "Invalid MP Configuration Table base checksum: %u\n",
485 Sum));
486 return NULL;
487 }
488
489 if (ConfigTable->ExtendedTableLength != 0)
490 {
491 /* FIXME: Check extended table */
492 }
493
494 return ConfigTable;
495 }
496
497
498 static BOOL
499 DetectMps(FRLDRHKEY CpuKey,
500 FRLDRHKEY FpuKey)
501 {
502 PMP_FLOATING_POINT_TABLE FpTable;
503 PMP_CONFIGURATION_TABLE ConfigTable;
504 PMP_PROCESSOR_ENTRY CpuEntry;
505 char *Ptr;
506 ULONG Offset;
507
508 /* Get floating point table */
509 FpTable = GetMpFloatingPointTable();
510 if (FpTable == NULL)
511 return FALSE;
512
513 DbgPrint((DPRINT_HWDETECT,
514 "MP Floating Point Table at: %x\n",
515 (ULONG)FpTable));
516
517 if (FpTable->FeatureByte[0] == 0)
518 {
519 /* Get configuration table */
520 ConfigTable = GetMpConfigurationTable(FpTable);
521 if (ConfigTable == NULL)
522 {
523 DbgPrint((DPRINT_HWDETECT,
524 "Failed to find the MP Configuration Table\n"));
525 return FALSE;
526 }
527
528 Offset = sizeof(MP_CONFIGURATION_TABLE);
529 while (Offset < ConfigTable->BaseTableLength)
530 {
531 Ptr = (char*)((ULONG)ConfigTable + Offset);
532
533 switch (*Ptr)
534 {
535 case 0:
536 CpuEntry = (PMP_PROCESSOR_ENTRY)Ptr;
537
538 DbgPrint((DPRINT_HWDETECT, "Processor Entry\n"));
539 DbgPrint((DPRINT_HWDETECT,
540 "APIC Id %u APIC Version %u Flags %x Signature %x Feature %x\n",
541 CpuEntry->LocalApicId,
542 CpuEntry->LocalApicVersion,
543 CpuEntry->CpuFlags,
544 CpuEntry->CpuSignature,
545 CpuEntry->FeatureFlags));
546 DbgPrint((DPRINT_HWDETECT,
547 "Processor %u: x86 Family %u Model %u Stepping %u\n",
548 CpuEntry->LocalApicId,
549 (ULONG)((CpuEntry->CpuSignature >> 8) & 0x0F),
550 (ULONG)((CpuEntry->CpuSignature >> 4) & 0x0F),
551 (ULONG)(CpuEntry->CpuSignature & 0x0F)));
552
553 SetMpsProcessor(CpuKey, FpuKey, CpuEntry);
554 Offset += 0x14;
555 break;
556
557 case 1:
558 DbgPrint((DPRINT_HWDETECT, "Bus Entry\n"));
559 Offset += 0x08;
560 break;
561
562 case 2:
563 DbgPrint((DPRINT_HWDETECT, "I/0 APIC Entry\n"));
564 Offset += 0x08;
565 break;
566
567 case 3:
568 DbgPrint((DPRINT_HWDETECT, "I/0 Interrupt Assignment Entry\n"));
569 Offset += 0x08;
570 break;
571
572 case 4:
573 DbgPrint((DPRINT_HWDETECT, "Local Interrupt Assignment Entry\n"));
574 Offset += 0x08;
575 break;
576
577 default:
578 DbgPrint((DPRINT_HWDETECT, "Unknown Entry %u\n",(ULONG)*Ptr));
579 return FALSE;
580 }
581 }
582 }
583 else
584 {
585 DbgPrint((DPRINT_HWDETECT,
586 "Unsupported MPS configuration: %x\n",
587 FpTable->FeatureByte[0]));
588
589 /* FIXME: Identify default configurations */
590
591 return FALSE;
592 }
593
594 return TRUE;
595 }
596
597
598
599 VOID
600 DetectCPUs(FRLDRHKEY SystemKey)
601 {
602 FRLDRHKEY CpuKey;
603 FRLDRHKEY FpuKey;
604 LONG Error;
605
606 /* Create the 'CentralProcessor' key */
607 Error = RegCreateKey(SystemKey,
608 L"CentralProcessor",
609 &CpuKey);
610 if (Error != ERROR_SUCCESS)
611 {
612 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
613 return;
614 }
615
616 /* Create the 'FloatingPointProcessor' key */
617 Error = RegCreateKey(SystemKey,
618 L"FloatingPointProcessor",
619 &FpuKey);
620 if (Error != ERROR_SUCCESS)
621 {
622 DbgPrint((DPRINT_HWDETECT, "RegCreateKey() failed (Error %u)\n", (int)Error));
623 return;
624 }
625
626 /* Detect CPUs */
627 if (!DetectMps(CpuKey, FpuKey))
628 {
629 DetectCPU(CpuKey, FpuKey);
630 }
631 }
632
633 /* EOF */