xref: /reactos/hal/halx86/mp/mpconfig.c (revision 25720d75)
1 /* $Id$
2  *
3  * COPYRIGHT:             See COPYING in the top level directory
4  * PROJECT:               ReactOS kernel
5  * FILE:                  hal/halx86/mp/mpconfig.c
6  * PURPOSE:
7  * PROGRAMMER:
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ******************************************************************/
17 
18 MP_FLOATING_POINTER* Mpf = NULL;
19 
20 /* FUNCTIONS ****************************************************************/
21 
22 static UCHAR
23 MPChecksum(PUCHAR Base,
24 	   ULONG Size)
25 /*
26  * Checksum an MP configuration block
27  */
28 {
29    UCHAR Sum = 0;
30 
31    while (Size--)
32       Sum += *Base++;
33 
34    return Sum;
35 }
36 
37 static VOID
38 HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
39 {
40   DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
41          " IRQ %02x, APIC ID %x, APIC INT %02x\n",
42          m->IrqType, m->IrqFlag & 3,
43          (m->IrqFlag >> 2) & 3, m->SrcBusId,
44          m->SrcBusIrq, m->DstApicId, m->DstApicInt);
45   if (IRQCount > MAX_IRQ_SOURCE)
46   {
47     DPRINT1("Max # of irq sources exceeded!!\n");
48     ASSERT(FALSE);
49   }
50 
51   IRQMap[IRQCount] = *m;
52   IRQCount++;
53 }
54 
55 PCHAR
56 HaliMPFamily(ULONG Family,
57 	     ULONG Model)
58 {
59    static CHAR str[64];
60    static PCHAR CPUs[] =
61    {
62       "80486DX", "80486DX",
63       "80486SX", "80486DX/2 or 80487",
64       "80486SL", "Intel5X2(tm)",
65       "Unknown", "Unknown",
66       "80486DX/4"
67    };
68    if (Family == 0x6)
69       return ("Pentium(tm) Pro");
70    if (Family == 0x5)
71       return ("Pentium(tm)");
72    if (Family == 0x0F && Model == 0x0F)
73       return("Special controller");
74    if (Family == 0x0F && Model == 0x00)
75       return("Pentium 4(tm)");
76    if (Family == 0x04 && Model < 9)
77       return CPUs[Model];
78    sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
79    return str;
80 }
81 
82 
83 static VOID
84 HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
85 {
86   UCHAR ver;
87 
88   if (!(m->CpuFlags & CPU_FLAG_ENABLED))
89     return;
90 
91   DPRINT("Processor #%d %s APIC version %d\n",
92          m->ApicId,
93          HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
94                       (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
95          m->ApicVersion);
96 
97   if (m->FeatureFlags & (1 << 0))
98     DPRINT("    Floating point unit present.\n");
99   if (m->FeatureFlags & (1 << 7))
100     DPRINT("    Machine Exception supported.\n");
101   if (m->FeatureFlags & (1 << 8))
102     DPRINT("    64 bit compare & exchange supported.\n");
103   if (m->FeatureFlags & (1 << 9))
104     DPRINT("    Internal APIC present.\n");
105   if (m->FeatureFlags & (1 << 11))
106     DPRINT("    SEP present.\n");
107   if (m->FeatureFlags & (1 << 12))
108     DPRINT("    MTRR present.\n");
109   if (m->FeatureFlags & (1 << 13))
110     DPRINT("    PGE  present.\n");
111   if (m->FeatureFlags & (1 << 14))
112     DPRINT("    MCA  present.\n");
113   if (m->FeatureFlags & (1 << 15))
114     DPRINT("    CMOV  present.\n");
115   if (m->FeatureFlags & (1 << 16))
116     DPRINT("    PAT  present.\n");
117   if (m->FeatureFlags & (1 << 17))
118     DPRINT("    PSE  present.\n");
119   if (m->FeatureFlags & (1 << 18))
120     DPRINT("    PSN  present.\n");
121   if (m->FeatureFlags & (1 << 19))
122     DPRINT("    Cache Line Flush Instruction present.\n");
123   /* 20 Reserved */
124   if (m->FeatureFlags & (1 << 21))
125     DPRINT("    Debug Trace and EMON Store present.\n");
126   if (m->FeatureFlags & (1 << 22))
127     DPRINT("    ACPI Thermal Throttle Registers present.\n");
128   if (m->FeatureFlags & (1 << 23))
129     DPRINT("    MMX  present.\n");
130   if (m->FeatureFlags & (1 << 24))
131     DPRINT("    FXSR  present.\n");
132   if (m->FeatureFlags & (1 << 25))
133     DPRINT("    XMM  present.\n");
134   if (m->FeatureFlags & (1 << 26))
135     DPRINT("    Willamette New Instructions present.\n");
136   if (m->FeatureFlags & (1 << 27))
137     DPRINT("    Self Snoop present.\n");
138   /* 28 Reserved */
139   if (m->FeatureFlags & (1 << 29))
140     DPRINT("    Thermal Monitor present.\n");
141   /* 30, 31 Reserved */
142 
143   CPUMap[CPUCount].APICId = m->ApicId;
144 
145   CPUMap[CPUCount].Flags = CPU_USABLE;
146 
147   if (m->CpuFlags & CPU_FLAG_BSP)
148   {
149     DPRINT("    Bootup CPU\n");
150     CPUMap[CPUCount].Flags |= CPU_BSP;
151     BootCPU = m->ApicId;
152   }
153 
154   if (m->ApicId > MAX_CPU)
155   {
156     DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
157     return;
158   }
159   ver = m->ApicVersion;
160 
161   /*
162    * Validate version
163    */
164   if (ver == 0x0)
165   {
166      DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
167      ver = 0x10;
168   }
169 //  ApicVersion[m->ApicId] = Ver;
170 //  BiosCpuApicId[CPUCount] = m->ApicId;
171   CPUMap[CPUCount].APICVersion = ver;
172 
173   CPUCount++;
174 }
175 
176 static VOID
177 HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
178 {
179   static UCHAR CurrentPCIBusId = 0;
180 
181   DPRINT("Bus #%d is %.*s\n", m->BusId, 6, m->BusType);
182 
183   if (strncmp(m->BusType, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0)
184   {
185      BUSMap[m->BusId] = MP_BUS_ISA;
186   }
187   else if (strncmp(m->BusType, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0)
188   {
189      BUSMap[m->BusId] = MP_BUS_EISA;
190   }
191   else if (strncmp(m->BusType, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0)
192   {
193      BUSMap[m->BusId] = MP_BUS_PCI;
194      PCIBUSMap[m->BusId] = CurrentPCIBusId;
195      CurrentPCIBusId++;
196   }
197   else if (strncmp(m->BusType, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0)
198   {
199      BUSMap[m->BusId] = MP_BUS_MCA;
200   }
201   else
202   {
203      DPRINT("Unknown bustype %.*s - ignoring\n", 6, m->BusType);
204   }
205 }
206 
207 static VOID
208 HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
209 {
210   if (!(m->ApicFlags & CPU_FLAG_ENABLED))
211     return;
212 
213   DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
214          m->ApicId, m->ApicVersion, m->ApicAddress);
215   if (IOAPICCount > MAX_IOAPIC)
216   {
217     DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
218            MAX_IOAPIC, IOAPICCount);
219     DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
220     ASSERT(FALSE);
221   }
222 
223   IOAPICMap[IOAPICCount].ApicId = m->ApicId;
224   IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
225   IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
226   IOAPICCount++;
227 }
228 
229 
230 static VOID
231 HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
232 {
233   DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
234          " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
235          m->IrqType, m->SrcBusIrq & 3,
236          (m->SrcBusIrq >> 2) & 3, m->SrcBusId,
237           m->SrcBusIrq, m->DstApicId, m->DstApicLInt);
238   /*
239    * Well it seems all SMP boards in existence
240    * use ExtINT/LVT1 == LINT0 and
241    * NMI/LVT2 == LINT1 - the following check
242    * will show us if this assumptions is false.
243    * Until then we do not have to add baggage.
244    */
245   if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0))
246   {
247     DPRINT1("Invalid MP table!\n");
248     ASSERT(FALSE);
249   }
250   if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1))
251   {
252     DPRINT1("Invalid MP table!\n");
253     ASSERT(FALSE);
254   }
255 }
256 
257 
258 static BOOLEAN
259 HaliReadMPConfigTable(PMP_CONFIGURATION_TABLE Table)
260 /*
261    PARAMETERS:
262       Table = Pointer to MP configuration table
263  */
264 {
265    PUCHAR Entry;
266    ULONG Count;
267 
268    if (Table->Signature != MPC_SIGNATURE)
269      {
270        PUCHAR pc = (PUCHAR)&Table->Signature;
271 
272        DPRINT1("Bad MP configuration block signature: %c%c%c%c\n",
273 		pc[0], pc[1], pc[2], pc[3]);
274        KeBugCheckEx(HAL_INITIALIZATION_FAILED, pc[0], pc[1], pc[2], pc[3]);
275        return FALSE;
276      }
277 
278    if (MPChecksum((PUCHAR)Table, Table->Length))
279      {
280        DPRINT1("Bad MP configuration block checksum\n");
281        ASSERT(FALSE);
282        return FALSE;
283      }
284 
285    if (Table->Specification != 0x01 && Table->Specification != 0x04)
286      {
287        DPRINT1("Bad MP configuration table version (%d)\n",
288 	       Table->Specification);
289        ASSERT(FALSE);
290        return FALSE;
291      }
292 
293    if (Table->LocalAPICAddress != APIC_DEFAULT_BASE)
294      {
295        DPRINT1("APIC base address is at 0x%X. I cannot handle non-standard adresses\n",
296 	       Table->LocalAPICAddress);
297        ASSERT(FALSE);
298        return FALSE;
299      }
300 
301    DPRINT("Oem: %.*s, ProductId: %.*s\n", 8, Table->Oem, 12, Table->ProductId);
302    DPRINT("APIC at: %08x\n", Table->LocalAPICAddress);
303 
304 
305    Entry = (PUCHAR)((ULONG_PTR)Table + sizeof(MP_CONFIGURATION_TABLE));
306    Count = 0;
307    while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
308    {
309      /* Switch on type */
310      switch (*Entry)
311        {
312        case MPCTE_PROCESSOR:
313          {
314 	   HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
315 	   Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
316 	   Count += sizeof(MP_CONFIGURATION_PROCESSOR);
317 	   break;
318 	 }
319        case MPCTE_BUS:
320 	 {
321 	   HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
322 	   Entry += sizeof(MP_CONFIGURATION_BUS);
323 	   Count += sizeof(MP_CONFIGURATION_BUS);
324 	   break;
325 	 }
326        case MPCTE_IOAPIC:
327 	 {
328 	   HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
329 	   Entry += sizeof(MP_CONFIGURATION_IOAPIC);
330 	   Count += sizeof(MP_CONFIGURATION_IOAPIC);
331 	   break;
332 	 }
333        case MPCTE_INTSRC:
334 	 {
335 	   HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
336 	   Entry += sizeof(MP_CONFIGURATION_INTSRC);
337 	   Count += sizeof(MP_CONFIGURATION_INTSRC);
338 	   break;
339 	 }
340        case MPCTE_LINTSRC:
341 	 {
342 	   HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
343 	   Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
344 	   Count += sizeof(MP_CONFIGURATION_INTLOCAL);
345 	   break;
346 	 }
347        default:
348 	 DPRINT1("Unknown entry in MPC table\n");
349 	 ASSERT(FALSE);
350 	 return FALSE;
351        }
352    }
353    return TRUE;
354 }
355 
356 static VOID
357 HaliConstructDefaultIOIrqMPTable(ULONG Type)
358 {
359 	MP_CONFIGURATION_INTSRC intsrc;
360 	UCHAR i;
361 
362 	intsrc.Type = MPCTE_INTSRC;
363 	intsrc.IrqFlag = 0;			/* conforming */
364 	intsrc.SrcBusId = 0;
365 	intsrc.DstApicId = IOAPICMap[0].ApicId;
366 
367 	intsrc.IrqType = INT_VECTORED;
368 	for (i = 0; i < 16; i++) {
369 		switch (Type) {
370 		case 2:
371 			if (i == 0 || i == 13)
372 				continue;	/* IRQ0 & IRQ13 not connected */
373 			/* Fall through */
374 		default:
375 			if (i == 2)
376 				continue;	/* IRQ2 is never connected */
377 		}
378 
379 		intsrc.SrcBusIrq = i;
380 		intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
381 		HaliMPIntSrcInfo(&intsrc);
382 	}
383 
384 	intsrc.IrqType = INT_EXTINT;
385 	intsrc.SrcBusIrq = 0;
386 	intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
387 	HaliMPIntSrcInfo(&intsrc);
388 }
389 
390 static VOID
391 HaliConstructDefaultISAMPTable(ULONG Type)
392 {
393   MP_CONFIGURATION_PROCESSOR processor;
394   MP_CONFIGURATION_BUS bus;
395   MP_CONFIGURATION_IOAPIC ioapic;
396   MP_CONFIGURATION_INTLOCAL lintsrc;
397   UCHAR linttypes[2] = { INT_EXTINT, INT_NMI };
398   UCHAR i;
399 
400   /*
401    * 2 CPUs, numbered 0 & 1.
402    */
403   processor.Type = MPCTE_PROCESSOR;
404   /* Either an integrated APIC or a discrete 82489DX. */
405   processor.ApicVersion = Type > 4 ? 0x10 : 0x01;
406   processor.CpuFlags = CPU_FLAG_ENABLED | CPU_FLAG_BSP;
407   /* FIXME: Get this from the bootstrap processor */
408   processor.CpuSignature = 0;
409   processor.FeatureFlags = 0;
410   processor.Reserved[0] = 0;
411   processor.Reserved[1] = 0;
412   for (i = 0; i < 2; i++)
413   {
414     processor.ApicId = i;
415     HaliMPProcessorInfo(&processor);
416     processor.CpuFlags &= ~CPU_FLAG_BSP;
417   }
418 
419   bus.Type = MPCTE_BUS;
420   bus.BusId = 0;
421   switch (Type)
422   {
423     default:
424     DPRINT("Unknown standard configuration %d\n", Type);
425       /* Fall through */
426     case 1:
427     case 5:
428       memcpy(bus.BusType, "ISA   ", 6);
429       break;
430     case 2:
431     case 6:
432     case 3:
433       memcpy(bus.BusType, "EISA  ", 6);
434       break;
435     case 4:
436     case 7:
437       memcpy(bus.BusType, "MCA   ", 6);
438   }
439   HaliMPBusInfo(&bus);
440   if (Type > 4)
441   {
442     bus.Type = MPCTE_BUS;
443     bus.BusId = 1;
444     memcpy(bus.BusType, "PCI   ", 6);
445     HaliMPBusInfo(&bus);
446   }
447 
448   ioapic.Type = MPCTE_IOAPIC;
449   ioapic.ApicId = 2;
450   ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
451   ioapic.ApicFlags = MP_IOAPIC_USABLE;
452   ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
453   HaliMPIOApicInfo(&ioapic);
454 
455   /*
456    * We set up most of the low 16 IO-APIC pins according to MPS rules.
457    */
458   HaliConstructDefaultIOIrqMPTable(Type);
459 
460   lintsrc.Type = MPCTE_LINTSRC;
461   lintsrc.IrqType = 0;
462   lintsrc.IrqFlag = 0;  /* conforming */
463   lintsrc.SrcBusId = 0;
464   lintsrc.SrcBusIrq = 0;
465   lintsrc.DstApicId = MP_APIC_ALL;
466   for (i = 0; i < 2; i++)
467   {
468     lintsrc.IrqType = linttypes[i];
469     lintsrc.DstApicLInt = i;
470     HaliMPIntLocalInfo(&lintsrc);
471   }
472 }
473 
474 
475 static BOOLEAN
476 HaliScanForMPConfigTable(ULONG Base,
477 			 ULONG Size)
478 {
479 /*
480    PARAMETERS:
481       Base = Base address of region
482       Size = Length of region to check
483    RETURNS:
484       TRUE if a valid MP configuration table was found
485  */
486 
487    PULONG bp = (PULONG)Base;
488    MP_FLOATING_POINTER* mpf;
489    UCHAR Checksum;
490 
491    while (Size > 0)
492    {
493       mpf = (MP_FLOATING_POINTER*)bp;
494       if (mpf->Signature == MPF_SIGNATURE)
495       {
496 	 Checksum = MPChecksum((PUCHAR)bp, 16);
497 	 DPRINT("Found MPF signature at %x, checksum %x\n", bp, Checksum);
498          if (Checksum == 0 &&
499 	     mpf->Length == 1)
500          {
501             DPRINT("Intel MultiProcessor Specification v1.%d compliant system.\n",
502                    mpf->Specification);
503 
504             if (mpf->Feature2 & FEATURE2_IMCRP)
505 	    {
506                DPRINT("Running in IMCR and PIC compatibility mode.\n");
507             }
508 	    else
509 	    {
510                DPRINT("Running in Virtual Wire compatibility mode.\n");
511 	    }
512 
513 
514             switch (mpf->Feature1)
515             {
516                case 0:
517                   /* Non standard configuration */
518                   break;
519                case 1:
520                   DPRINT("ISA\n");
521                   break;
522                case 2:
523                   DPRINT("EISA with no IRQ8 chaining\n");
524                   break;
525                case 3:
526                   DPRINT("EISA\n");
527                   break;
528                case 4:
529                   DPRINT("MCA\n");
530                   break;
531                case 5:
532                   DPRINT("ISA and PCI\n");
533                   break;
534                case 6:
535                   DPRINT("EISA and PCI\n");
536                   break;
537                case 7:
538                   DPRINT("MCA and PCI\n");
539                   break;
540                default:
541                   DPRINT("Unknown standard configuration %d\n", mpf->Feature1);
542                   return FALSE;
543             }
544             Mpf = mpf;
545             return TRUE;
546          }
547       }
548       bp += 4;
549       Size -= 16;
550    }
551    return FALSE;
552 }
553 
554 static BOOLEAN
555 HaliGetSmpConfig(VOID)
556 {
557    if (Mpf == NULL)
558    {
559       return FALSE;
560    }
561 
562    if (Mpf->Feature2 & FEATURE2_IMCRP)
563    {
564       DPRINT("Running in IMCR and PIC compatibility mode.\n");
565       APICMode = amPIC;
566    }
567    else
568    {
569       DPRINT("Running in Virtual Wire compatibility mode.\n");
570       APICMode = amVWIRE;
571    }
572 
573    if (Mpf->Feature1 == 0 && Mpf->Address)
574    {
575       if(!HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)Mpf->Address))
576       {
577          DPRINT("BIOS bug, MP table errors detected!...\n");
578 	 DPRINT("... disabling SMP support. (tell your hw vendor)\n");
579 	 return FALSE;
580       }
581       if (IRQCount == 0)
582       {
583          MP_CONFIGURATION_BUS bus;
584 
585          DPRINT("BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n");
586 
587          bus.BusId = 1;
588 	 memcpy(bus.BusType, "ISA   ", 6);
589          HaliMPBusInfo(&bus);
590 	 HaliConstructDefaultIOIrqMPTable(bus.BusId);
591       }
592 
593    }
594    else if(Mpf->Feature1 != 0)
595    {
596       HaliConstructDefaultISAMPTable(Mpf->Feature1);
597    }
598    else
599    {
600       ASSERT(FALSE);
601    }
602    return TRUE;
603 }
604 
605 BOOLEAN
606 HaliFindSmpConfig(VOID)
607 {
608    /*
609      Scan the system memory for an MP configuration table
610        1) Scan the first KB of system base memory
611        2) Scan the last KB of system base memory
612        3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
613        4) Scan the first KB from the Extended BIOS Data Area
614    */
615 
616    if (!HaliScanForMPConfigTable(0x0, 0x400))
617    {
618       if (!HaliScanForMPConfigTable(0x9FC00, 0x400))
619       {
620          if (!HaliScanForMPConfigTable(0xF0000, 0x10000))
621          {
622             if (!HaliScanForMPConfigTable(*((PUSHORT)0x040E) << 4, 0x400))
623 	    {
624                DPRINT("No multiprocessor compliant system found.\n");
625                return FALSE;
626             }
627          }
628       }
629    }
630 
631    if (HaliGetSmpConfig())
632    {
633       return TRUE;
634    }
635    else
636    {
637       DPRINT("No MP config table found\n");
638       return FALSE;
639    }
640 
641 }
642 
643 /* EOF */
644