608f6d746a6c55abff023d1ba9ec39dee8dd9fee
[reactos.git] / hal / halx86 / mp.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            ntoskrnl/hal/x86/mp.c
6  * PURPOSE:         Intel MultiProcessor specification support
7  * PROGRAMMER:      David Welch (welch@cwcom.net)
8  *                  Casper S. Hornstrup (chorns@users.sourceforge.net)
9  * NOTES:           Parts adapted from linux SMP code
10  * UPDATE HISTORY:
11  *     22/05/1998  DW   Created
12  *     12/04/2001  CSH  Added MultiProcessor specification support
13  */
14
15 /* INCLUDES *****************************************************************/
16
17 #include <ddk/ntddk.h>
18 #include <roscfg.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 #ifdef MP
24
25 #include <hal.h>
26 #include <mps.h>
27
28 #include <internal/ntoskrnl.h>
29 #include <internal/i386/segment.h>
30 #include <internal/ke.h>
31 #include <internal/ps.h>
32
33 /*
34    Address of area to be used for communication between Application
35    Processors (APs) and the BootStrap Processor (BSP)
36  */
37 #define COMMON_AREA  0x2000
38
39 #define BIOS_AREA    0x0
40
41 typedef struct __attribute__((packed)) _COMMON_AREA_INFO
42 {
43    ULONG Stack;      /* Location of AP stack */
44    ULONG Debug[16];  /* For debugging */
45 } COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
46
47 CPU_INFO CPUMap[MAX_CPU];          /* Map of all CPUs in the system */
48 ULONG CPUCount;                    /* Total number of CPUs */
49 ULONG OnlineCPUs;                  /* Bitmask of online CPUs */
50
51 UCHAR BUSMap[MAX_BUS];             /* Map of all buses in the system */
52 UCHAR PCIBUSMap[MAX_BUS];          /* Map of all PCI buses in the system */
53
54 IOAPIC_INFO IOAPICMap[MAX_IOAPIC]; /* Map of all I/O APICs in the system */
55 ULONG IOAPICCount;                 /* Number of I/O APICs in the system */
56
57 MP_CONFIGURATION_INTSRC IRQMap[MAX_IRQ_SOURCE]; /* Map of all IRQs */
58 ULONG IRQVectorMap[MAX_IRQ_SOURCE];             /* IRQ to vector map */
59 ULONG IRQCount;                                 /* Number of IRQs  */
60
61 ULONG APICMode;                     /* APIC mode at startup */
62 ULONG BootCPU;                      /* Bootstrap processor */
63 ULONG NextCPU;                      /* Next CPU to start */
64 PULONG BIOSBase;                    /* Virtual address of BIOS data segment */
65 PULONG APICBase;                    /* Virtual address of local APIC */
66 PULONG CommonBase;                  /* Virtual address of common area */
67
68 extern CHAR *APstart, *APend;
69 extern VOID (*APflush)(VOID);
70
71 extern VOID MpsTimerInterrupt(VOID);
72 extern VOID MpsErrorInterrupt(VOID);
73 extern VOID MpsSpuriousInterrupt(VOID);
74
75 #define CMOS_READ(address) ({ \
76    WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
77    READ_PORT_UCHAR((PUCHAR)0x71)); \
78 })
79
80 #define CMOS_WRITE(address, value) ({ \
81    WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
82    WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
83 })
84
85 BOOLEAN MPSInitialized = FALSE;  /* Is the MP system initialized? */
86
87 VOID APICDisable(VOID);
88 static VOID APICSyncArbIDs(VOID);
89
90 /* For debugging */
91 ULONG lastregr = 0;
92 ULONG lastvalr = 0;
93 ULONG lastregw = 0;
94 ULONG lastvalw = 0;
95
96 #endif /* MP */
97
98
99 BOOLEAN BSPInitialized = FALSE;  /* Is the BSP initialized? */
100
101
102 /* FUNCTIONS *****************************************************************/
103
104 #ifdef MP
105
106 /* Functions for handling 8259A PICs */
107
108 VOID Disable8259AIrq(
109   ULONG irq)
110 {
111         ULONG tmp;
112
113         if (irq & 8) {
114     tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
115     tmp |= (1 << irq);
116     WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
117         } else {
118     tmp = READ_PORT_UCHAR((PUCHAR)0x21);
119     tmp |= (1 << irq);
120     WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
121   }
122 }
123
124
125 VOID Enable8259AIrq(
126   ULONG irq)
127 {
128   ULONG tmp;
129
130         if (irq & 8) {
131     tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
132     tmp &= ~(1 << irq);
133     WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
134         } else {
135     tmp = READ_PORT_UCHAR((PUCHAR)0x21);
136     tmp &= ~(1 << irq);
137     WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
138   }
139 }
140
141
142 /* Functions for handling I/O APICs */
143
144 volatile ULONG IOAPICRead(
145    ULONG Apic,
146    ULONG Offset)
147 {
148   PULONG Base;
149
150   Base = (PULONG)IOAPICMap[Apic].ApicAddress;
151
152         *Base = Offset;
153         return *((PULONG)((ULONG)Base + IOAPIC_IOWIN));
154 }
155
156
157 VOID IOAPICWrite(
158    ULONG Apic,
159    ULONG Offset,
160    ULONG Value)
161 {
162   PULONG Base;
163
164   Base = (PULONG)IOAPICMap[Apic].ApicAddress;
165
166   *Base = Offset;
167         *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value;
168 }
169
170
171 VOID IOAPICClearPin(
172   ULONG Apic,
173   ULONG Pin)
174 {
175   IOAPIC_ROUTE_ENTRY Entry;
176
177   /*
178    * Disable it in the IO-APIC irq-routing table
179    */
180         memset(&Entry, 0, sizeof(Entry));
181         Entry.mask = 1;
182         IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0));
183         IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1));
184 }
185
186 static VOID IOAPICClear(
187   ULONG Apic)
188 {
189         ULONG Pin;
190
191   for (Pin = 0; Pin < IOAPICMap[Apic].EntryCount; Pin++)
192                 IOAPICClearPin(Apic, Pin);
193 }
194
195 static VOID IOAPICClearAll(
196   VOID)
197 {
198   ULONG Apic;
199
200         for (Apic = 0; Apic < IOAPICCount; Apic++)
201                 IOAPICClear(Apic);
202 }
203
204 /* This is performance critical and should probably be done in assembler */
205 VOID IOAPICMaskIrq(
206   ULONG Apic,
207   ULONG Irq)
208 {
209   IOAPIC_ROUTE_ENTRY Entry;
210
211         *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
212   Entry.mask = 1;
213         IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
214 }
215
216
217 /* This is performance critical and should probably be done in assembler */
218 VOID IOAPICUnmaskIrq(
219   ULONG Apic,
220   ULONG Irq)
221 {
222   IOAPIC_ROUTE_ENTRY Entry;
223
224   *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
225   Entry.mask = 0;
226   IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
227 }
228
229 static VOID 
230 IOAPICSetupIds(VOID)
231 {
232   ULONG tmp, apic, i;
233   UCHAR old_id;
234   
235   /*
236    * Set the IOAPIC ID to the value stored in the MPC table.
237    */
238   for (apic = 0; apic < IOAPICCount; apic++) {
239     
240     /* Read the register 0 value */
241     tmp = IOAPICRead(apic, IOAPIC_ID);
242     
243     old_id = IOAPICMap[apic].ApicId;
244     
245     if (IOAPICMap[apic].ApicId >= 0xf) {
246       DPRINT1("BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",
247               apic, IOAPICMap[apic].ApicId);
248       DPRINT1("... fixing up to %d. (tell your hw vendor)\n", 
249               GET_IOAPIC_ID(tmp));
250       IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp);
251     }
252     
253     /*
254      * We need to adjust the IRQ routing table
255      * if the ID changed.
256      */
257     if (old_id != IOAPICMap[apic].ApicId)
258       for (i = 0; i < IRQCount; i++)
259         if (IRQMap[i].DstApicId == old_id)
260           IRQMap[i].DstApicId = IOAPICMap[apic].ApicId;
261     
262     /*
263      * Read the right value from the MPC table and
264      * write it into the ID register.
265      */
266     DPRINT("Changing IO-APIC physical APIC ID to %d\n",
267            IOAPICMap[apic].ApicId);
268     
269     tmp &= ~IOAPIC_ID_MASK;
270     tmp |= SET_IOAPIC_ID(IOAPICMap[apic].ApicId);
271     
272     IOAPICWrite(apic, IOAPIC_ID, tmp);
273     
274     /*
275      * Sanity check
276      */
277     tmp = IOAPICRead(apic, 0);
278     if (GET_IOAPIC_ID(tmp) != IOAPICMap[apic].ApicId) {
279       DPRINT1("Could not set I/O APIC ID!\n");
280       KeBugCheck(0);
281     }
282   }
283 }
284
285
286 /*
287  * EISA Edge/Level control register, ELCR
288  */
289 static ULONG EISA_ELCR(
290   ULONG irq)
291 {
292         if (irq < 16) {
293                 PUCHAR port = (PUCHAR)(0x4d0 + (irq >> 3));
294                 return (READ_PORT_UCHAR(port) >> (irq & 7)) & 1;
295         }
296         DPRINT("Broken MPtable reports ISA irq %d\n", irq);
297         return 0;
298 }
299
300 /* EISA interrupts are always polarity zero and can be edge or level
301  * trigger depending on the ELCR value.  If an interrupt is listed as
302  * EISA conforming in the MP table, that means its trigger type must
303  * be read in from the ELCR */
304
305 #define default_EISA_trigger(idx)       (EISA_ELCR(IRQMap[idx].SrcBusIrq))
306 #define default_EISA_polarity(idx)      (0)
307
308 /* ISA interrupts are always polarity zero edge triggered,
309  * when listed as conforming in the MP table. */
310
311 #define default_ISA_trigger(idx)        (0)
312 #define default_ISA_polarity(idx)       (0)
313
314 /* PCI interrupts are always polarity one level triggered,
315  * when listed as conforming in the MP table. */
316
317 #define default_PCI_trigger(idx)        (1)
318 #define default_PCI_polarity(idx)       (1)
319
320 /* MCA interrupts are always polarity zero level triggered,
321  * when listed as conforming in the MP table. */
322
323 #define default_MCA_trigger(idx)        (1)
324 #define default_MCA_polarity(idx)       (0)
325
326 static ULONG IRQPolarity(
327   ULONG idx)
328 {
329         ULONG bus = IRQMap[idx].SrcBusId;
330         ULONG polarity;
331
332         /*
333          * Determine IRQ line polarity (high active or low active):
334          */
335         switch (IRQMap[idx].IrqFlag & 3)
336         {
337                 case 0: /* conforms, ie. bus-type dependent polarity */
338                 {
339                         switch (BUSMap[bus])
340                         {
341                                 case MP_BUS_ISA: /* ISA pin */
342                                 {
343                                         polarity = default_ISA_polarity(idx);
344                                         break;
345                                 }
346                                 case MP_BUS_EISA: /* EISA pin */
347                                 {
348                                         polarity = default_EISA_polarity(idx);
349                                         break;
350                                 }
351                                 case MP_BUS_PCI: /* PCI pin */
352                                 {
353                                         polarity = default_PCI_polarity(idx);
354                                         break;
355                                 }
356                                 case MP_BUS_MCA: /* MCA pin */
357                                 {
358                                         polarity = default_MCA_polarity(idx);
359                                         break;
360                                 }
361                                 default:
362                                 {
363                                         DPRINT("Broken BIOS!!\n");
364                                         polarity = 1;
365                                         break;
366                                 }
367                         }
368                         break;
369                 }
370                 case 1: /* high active */
371                 {
372                         polarity = 0;
373                         break;
374                 }
375                 case 2: /* reserved */
376                 {
377                         DPRINT("Broken BIOS!!\n");
378                         polarity = 1;
379                         break;
380                 }
381                 case 3: /* low active */
382                 {
383                         polarity = 1;
384                         break;
385                 }
386                 default: /* invalid */
387                 {
388                         DPRINT("Broken BIOS!!\n");
389                         polarity = 1;
390                         break;
391                 }
392         }
393         return polarity;
394 }
395
396 static ULONG IRQTrigger(
397   ULONG idx)
398 {
399         ULONG bus = IRQMap[idx].SrcBusId;
400         ULONG trigger;
401
402         /*
403          * Determine IRQ trigger mode (edge or level sensitive):
404          */
405         switch ((IRQMap[idx].IrqFlag >> 2) & 3)
406         {
407                 case 0: /* conforms, ie. bus-type dependent */
408                 {
409                         switch (BUSMap[bus])
410                         {
411                                 case MP_BUS_ISA: /* ISA pin */
412                                 {
413                                         trigger = default_ISA_trigger(idx);
414                                         break;
415                                 }
416                                 case MP_BUS_EISA: /* EISA pin */
417                                 {
418                                         trigger = default_EISA_trigger(idx);
419                                         break;
420                                 }
421                                 case MP_BUS_PCI: /* PCI pin */
422                                 {
423                                         trigger = default_PCI_trigger(idx);
424                                         break;
425                                 }
426                                 case MP_BUS_MCA: /* MCA pin */
427                                 {
428                                         trigger = default_MCA_trigger(idx);
429                                         break;
430                                 }
431                                 default:
432                                 {
433                                         DPRINT("Broken BIOS!!\n");
434                                         trigger = 1;
435                                         break;
436                                 }
437                         }
438                         break;
439                 }
440                 case 1: /* edge */
441                 {
442                         trigger = 0;
443                         break;
444                 }
445                 case 2: /* reserved */
446                 {
447                         DPRINT("Broken BIOS!!\n");
448                         trigger = 1;
449                         break;
450                 }
451                 case 3: /* level */
452                 {
453                         trigger = 1;
454                         break;
455                 }
456                 default: /* invalid */
457                 {
458                         DPRINT("Broken BIOS!!\n");
459                         trigger = 0;
460                         break;
461                 }
462         }
463         return trigger;
464 }
465
466
467 static ULONG Pin2Irq(
468   ULONG idx,
469   ULONG apic,
470   ULONG pin)
471 {
472         ULONG irq, i;
473         ULONG bus = IRQMap[idx].SrcBusId;
474
475         /*
476          * Debugging check, we are in big trouble if this message pops up!
477          */
478         if (IRQMap[idx].DstApicInt != pin) {
479                 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\n");
480   }
481
482         switch (BUSMap[bus])
483         {
484                 case MP_BUS_ISA: /* ISA pin */
485                 case MP_BUS_EISA:
486                 case MP_BUS_MCA:
487                 {
488                         irq = IRQMap[idx].SrcBusIrq;
489                         break;
490                 }
491                 case MP_BUS_PCI: /* PCI pin */
492                 {
493                         /*
494                          * PCI IRQs are mapped in order
495                          */
496                         i = irq = 0;
497                         while (i < apic)
498                                 irq += IOAPICMap[i++].EntryCount;
499                         irq += pin;
500                         break;
501                 }
502                 default:
503                 {
504                         DPRINT("Unknown bus type %d.\n",bus);
505                         irq = 0;
506                         break;
507                 }
508         }
509
510         return irq;
511 }
512
513
514 /*
515  * Rough estimation of how many shared IRQs there are, can
516  * be changed anytime.
517  */
518 #define MAX_PLUS_SHARED_IRQS PIC_IRQS
519 #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + PIC_IRQS)
520
521 /*
522  * This is performance-critical, we want to do it O(1)
523  *
524  * the indexing order of this array favors 1:1 mappings
525  * between pins and IRQs.
526  */
527
528 static struct irq_pin_list {
529         ULONG apic, pin, next;
530 } irq_2_pin[PIN_MAP_SIZE];
531
532 /*
533  * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
534  * shared ISA-space IRQs, so we have to support them. We are super
535  * fast in the common case, and fast for shared ISA-space IRQs.
536  */
537 static VOID AddPinToIrq(
538   ULONG irq,
539   ULONG apic,
540   ULONG pin)
541 {
542         static ULONG first_free_entry = PIC_IRQS;
543         struct irq_pin_list *entry = irq_2_pin + irq;
544
545         while (entry->next)
546                 entry = irq_2_pin + entry->next;
547
548         if (entry->pin != -1) {
549                 entry->next = first_free_entry;
550                 entry = irq_2_pin + entry->next;
551                 if (++first_free_entry >= PIN_MAP_SIZE) {
552       DPRINT1("Ohh no!");
553                         KeBugCheck(0);
554      }
555         }
556         entry->apic = apic;
557         entry->pin = pin;
558 }
559
560
561 /*
562  * Find the IRQ entry number of a certain pin.
563  */
564 static ULONG IOAPICGetIrqEntry(
565   ULONG apic,
566   ULONG pin,
567   ULONG type)
568 {
569         ULONG i;
570
571         for (i = 0; i < IRQCount; i++)
572                 if (IRQMap[i].IrqType == type &&
573                     (IRQMap[i].DstApicId == IOAPICMap[apic].ApicId ||
574                      IRQMap[i].DstApicId == MP_APIC_ALL) &&
575                      IRQMap[i].DstApicInt == pin)
576                         return i;
577
578         return -1;
579 }
580
581
582 static ULONG AssignIrqVector(
583   ULONG irq)
584 {
585         static ULONG current_vector = FIRST_DEVICE_VECTOR, vector_offset = 0;
586   ULONG vector;
587
588   /* There may already have been assigned a vector for this IRQ */
589   vector = IRQVectorMap[irq];
590         if (vector > 0)
591                 return vector;
592
593   current_vector += 8;
594   if (current_vector > FIRST_SYSTEM_VECTOR) {
595                 vector_offset++;
596           current_vector = FIRST_DEVICE_VECTOR + vector_offset;
597   } else if (current_vector == FIRST_SYSTEM_VECTOR) {
598      DPRINT1("Ran out of interrupt sources!");
599      KeBugCheck(0);
600   }
601
602         IRQVectorMap[irq] = current_vector;
603         return current_vector;
604 }
605
606
607 VOID IOAPICSetupIrqs(
608   VOID)
609 {
610         IOAPIC_ROUTE_ENTRY entry;
611         ULONG apic, pin, idx, irq, first_notcon = 1, vector;
612
613         DPRINT("Init IO_APIC IRQs\n");
614
615         for (apic = 0; apic < IOAPICCount; apic++) {
616         for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++) {
617
618                 /*
619                  * add it to the IO-APIC irq-routing table
620                  */
621                 memset(&entry,0,sizeof(entry));
622
623                 entry.delivery_mode = APIC_DM_LOWEST;
624                 entry.dest_mode = 1;  /* logical delivery */
625                 entry.mask = 0;       /* enable IRQ */
626                 entry.dest.logical.logical_dest = OnlineCPUs;
627
628                 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED);
629                 if (idx == -1) {
630                         if (first_notcon) {
631                                 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap[apic].ApicId, pin);
632                                 first_notcon = 0;
633                         } else {
634                                 DPRINT(", %d-%d\n", IOAPICMap[apic].ApicId, pin);
635       }
636                         continue;
637                 }
638
639                 entry.trigger = IRQTrigger(idx);
640                 entry.polarity = IRQPolarity(idx);
641
642                 if (entry.trigger) {
643                         entry.trigger = 1;
644                         entry.mask = 1;
645                         entry.dest.logical.logical_dest = OnlineCPUs;
646                 }
647
648                 irq = Pin2Irq(idx, apic, pin);
649                 AddPinToIrq(irq, apic, pin);
650
651         vector = AssignIrqVector(irq);
652                 entry.vector = vector;
653
654     if (irq == 0)
655     {
656       /* Mask timer IRQ */
657       entry.mask = 1;
658     }
659
660     if ((apic == 0) && (irq < 16))
661                   Disable8259AIrq(irq);
662
663     IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1));
664                 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0));
665         }
666         }
667 }
668
669
670 static VOID IOAPICEnable(
671   VOID)
672 {
673         ULONG i, tmp;
674
675         for (i = 0; i < PIN_MAP_SIZE; i++) {
676                 irq_2_pin[i].pin = -1;
677                 irq_2_pin[i].next = 0;
678         }
679
680         /*
681          * The number of IO-APIC IRQ registers (== #pins):
682          */
683   for (i = 0; i < IOAPICCount; i++) {
684                 tmp = IOAPICRead(i, IOAPIC_VER);
685                 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1;
686         }
687
688         /*
689          * Do not trust the IO-APIC being empty at bootup
690          */
691         IOAPICClearAll();
692 }
693
694 #if 0
695 static VOID IOAPICDisable(
696   VOID)
697 {
698         /*
699          * Clear the IO-APIC before rebooting
700          */
701         IOAPICClearAll();
702
703         APICDisable();
704 }
705 #endif
706
707
708 static VOID IOAPICSetup(
709   VOID)
710 {
711   IOAPICEnable();
712   IOAPICSetupIds();
713   if (0) {
714     // FIXME: This causes application processors to not boot if asked to
715     APICSyncArbIDs();
716   }
717   IOAPICSetupIrqs();
718 }
719
720
721 VOID IOAPICDump(VOID)
722 {
723         ULONG apic, i;
724   ULONG reg0, reg1, reg2;
725
726         DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount);
727         for (i = 0; i < IOAPICCount; i++) {
728                 DbgPrint("Number of IO-APIC #%d registers: %d.\n",
729                         IOAPICMap[i].ApicId,
730             IOAPICMap[i].EntryCount);
731   }
732
733         /*
734          * We are a bit conservative about what we expect.  We have to
735          * know about every hardware change ASAP.
736          */
737         DbgPrint("Testing the IO APIC.......................\n");
738
739         for (apic = 0; apic < IOAPICCount; apic++) {
740
741   reg0 = IOAPICRead(apic, IOAPIC_ID);
742         reg1 = IOAPICRead(apic, IOAPIC_VER);
743         if (GET_IOAPIC_VERSION(reg1) >= 0x10) {
744     reg2 = IOAPICRead(apic, IOAPIC_ARB);
745   }
746
747         DbgPrint("\n");
748         DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId);
749         DbgPrint(".... register #00: %08X\n", reg0);
750         DbgPrint(".......    : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0));
751         if (reg0 & 0xF0FFFFFF) {
752     DbgPrint("  WARNING: Unexpected IO-APIC\n");
753   }
754
755         DbgPrint(".... register #01: %08X\n", reg1);
756   i = GET_IOAPIC_MRE(reg1);
757
758         DbgPrint(".......     : max redirection entries: %04X\n", i);
759         if ((i != 0x0f) && /* older (Neptune) boards */
760                 (i != 0x17) &&   /* typical ISA+PCI boards */
761                 (i != 0x1b) &&   /* Compaq Proliant boards */
762                 (i != 0x1f) &&   /* dual Xeon boards */
763                 (i != 0x22) &&   /* bigger Xeon boards */
764                 (i != 0x2E) &&
765                 (i != 0x3F)) {
766     DbgPrint("  WARNING: Unexpected IO-APIC\n");
767   }
768
769   i =   GET_IOAPIC_VERSION(reg1);
770   DbgPrint(".......     : IO APIC version: %04X\n", i);
771         if ((i != 0x01) && /* 82489DX IO-APICs */
772                 (i != 0x10) &&   /* oldest IO-APICs */
773                 (i != 0x11) &&   /* Pentium/Pro IO-APICs */
774                 (i != 0x13)) {   /* Xeon IO-APICs */
775     DbgPrint("  WARNING: Unexpected IO-APIC\n");
776   }
777
778         if (reg1 & 0xFF00FF00) {
779     DbgPrint("  WARNING: Unexpected IO-APIC\n");
780   }
781
782         if (GET_IOAPIC_VERSION(reg1) >= 0x10) {
783                 DbgPrint(".... register #02: %08X\n", reg2);
784                 DbgPrint(".......     : arbitration: %02X\n",
785       GET_IOAPIC_ARB(reg2));
786         if (reg2 & 0xF0FFFFFF) {
787       DbgPrint("  WARNING: Unexpected IO-APIC\n");
788     }
789         }
790
791         DbgPrint(".... IRQ redirection table:\n");
792   DbgPrint(" NR Log Phy Mask Trig IRR Pol"
793                           " Stat Dest Deli Vect:   \n");
794
795         for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++) {
796                 IOAPIC_ROUTE_ENTRY entry;
797
798                 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2);
799                 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2);
800
801                 DbgPrint(" %02x %03X %02X  ",
802                         i,
803                         entry.dest.logical.logical_dest,
804                         entry.dest.physical.physical_dest
805                 );
806
807   DbgPrint("%C    %C    %1d  %C    %C    %C     %03X    %02X\n",
808                         (entry.mask == 0) ? 'U' : 'M',            // Unmasked/masked
809                         (entry.trigger == 0) ? 'E' : 'L',         // Edge/level sensitive
810                         entry.irr,
811                         (entry.polarity == 0) ? 'H' : 'L',        // Active high/active low
812                         (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending
813                         (entry.dest_mode == 0) ? 'P' : 'L',       // Physical logical
814                         entry.delivery_mode,
815                         entry.vector
816                 );
817         }
818         }
819         DbgPrint("IRQ to pin mappings:\n");
820         for (i = 0; i < PIC_IRQS; i++) {
821                 struct irq_pin_list *entry = irq_2_pin + i;
822                 if (entry->pin < 0)
823                         continue;
824                 DbgPrint("IRQ%d ", i);
825                 for (;;) {
826                         DbgPrint("-> %d", entry->pin);
827                         if (!entry->next)
828                                 break;
829                         entry = irq_2_pin + entry->next;
830                 }
831     if (i % 2) {
832       DbgPrint("\n");
833     } else {
834       DbgPrint("        ");
835     }
836         }
837
838         DbgPrint(".................................... done.\n");
839 }
840
841
842
843 /* Functions for handling local APICs */
844
845 #if 0
846 volatile inline ULONG APICRead(
847    ULONG Offset)
848 {
849    PULONG p;
850
851    p = (PULONG)((ULONG)APICBase + Offset);
852    return *p;
853 }
854 #else
855 volatile inline ULONG APICRead(
856    ULONG Offset)
857 {
858    PULONG p;
859
860    lastregr = Offset;
861    lastvalr = 0;
862
863    //DPRINT1("R(0x%X)", Offset);
864    p = (PULONG)((ULONG)APICBase + Offset);
865    lastvalr = *p;
866    //DPRINT1("(0x%08X)\n", *p);
867
868    return lastvalr;
869 }
870 #endif
871
872 #if 0
873 inline VOID APICWrite(
874    ULONG Offset,
875    ULONG Value)
876 {
877    PULONG p;
878
879    p = (PULONG)((ULONG)APICBase + Offset);
880
881    *p = Value;
882 }
883 #else
884 inline VOID APICWrite(
885    ULONG Offset,
886    ULONG Value)
887 {
888    PULONG p;
889
890    lastregw = Offset;
891    lastvalw = Value;
892
893    //DPRINT1("W(0x%X, 0x%08X)\n", Offset, Value);
894    p = (PULONG)((ULONG)APICBase + Offset);
895
896    *p = Value;
897 }
898 #endif
899
900
901 inline VOID APICSendEOI(VOID)
902 {
903   // Dummy read
904   APICRead(APIC_SIVR);
905   // Send the EOI
906   APICWrite(APIC_EOI, 0);
907 }
908
909
910 VOID DumpESR(VOID)
911 {
912   ULONG tmp;
913
914   if (CPUMap[ThisCPU()].MaxLVT > 3)
915     APICWrite(APIC_ESR, 0);
916   tmp = APICRead(APIC_ESR);
917   DbgPrint("ESR %08x\n", tmp);
918 }
919
920
921 ULONG APICGetMaxLVT(VOID)
922 {
923         ULONG tmp, ver, maxlvt;
924
925   tmp = APICRead(APIC_VER);
926         ver = GET_APIC_VERSION(tmp);
927         /* 82489DXs do not report # of LVT entries. */
928         maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(tmp) : 2;
929
930         return maxlvt;
931 }
932
933
934 static VOID APICClear(
935   VOID)
936 {
937   ULONG tmp, maxlvt;
938
939   maxlvt = CPUMap[ThisCPU()].MaxLVT;
940
941   /*
942    * Careful: we have to set masks only first to deassert
943    * any level-triggered sources.
944    */
945   tmp = APICRead(APIC_LVTT);
946   APICWrite(APIC_LVTT, tmp | APIC_LVT_MASKED);
947
948   tmp = APICRead(APIC_LINT0);
949   APICWrite(APIC_LINT0, tmp | APIC_LVT_MASKED);
950
951   tmp = APICRead(APIC_LINT1);
952   APICWrite(APIC_LINT1, tmp | APIC_LVT_MASKED);
953
954   if (maxlvt >= 3) {
955     tmp = APICRead(APIC_LVT3);
956     APICWrite(APIC_LVT3, tmp | APIC_LVT3_MASKED);
957   }
958   
959   if (maxlvt >= 4) {
960     tmp = APICRead(APIC_LVTPC);
961     APICWrite(APIC_LVTPC, tmp | APIC_LVT_MASKED);
962   }
963
964   /*
965    * Clean APIC state for other OSs:
966    */
967   APICRead(APIC_SIVR);    // Dummy read
968   APICWrite(APIC_LVTT, APIC_LVT_MASKED);
969
970   APICRead(APIC_SIVR);    // Dummy read
971   APICWrite(APIC_LINT0, APIC_LVT_MASKED);
972
973   APICRead(APIC_SIVR);    // Dummy read
974   APICWrite(APIC_LINT1, APIC_LVT_MASKED);
975
976   if (maxlvt >= 3) {
977     APICRead(APIC_SIVR);  // Dummy read
978     APICWrite(APIC_LVT3, APIC_LVT3_MASKED);
979   }
980
981   if (maxlvt >= 4) {
982     APICRead(APIC_SIVR);  // Dummy read
983     APICWrite(APIC_LVTPC, APIC_LVT_MASKED);
984   }
985 }
986
987 /* Enable symetric I/O mode ie. connect the BSP's
988    local APIC to INT and NMI lines */
989 inline VOID EnableSMPMode(
990    VOID)
991 {
992    /*
993     * Do not trust the local APIC being empty at bootup.
994     */
995    APICClear();
996
997    WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
998    WRITE_PORT_UCHAR((PUCHAR)0x23, 0x01);
999 }
1000
1001
1002 /* Disable symetric I/O mode ie. go to PIC mode */
1003 inline VOID DisableSMPMode(
1004    VOID)
1005 {
1006    /*
1007     * Put the board back into PIC mode (has an effect
1008     * only on certain older boards).  Note that APIC
1009     * interrupts, including IPIs, won't work beyond
1010     * this point!  The only exception are INIT IPIs.
1011     */
1012    WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
1013    WRITE_PORT_UCHAR((PUCHAR)0x23, 0x00);
1014 }
1015
1016
1017 VOID APICDisable(
1018   VOID)
1019 {
1020   ULONG tmp;
1021
1022   APICClear();
1023
1024   /*
1025    * Disable APIC (implies clearing of registers for 82489DX!).
1026    */
1027   tmp = APICRead(APIC_SIVR);
1028   tmp &= ~APIC_SIVR_ENABLE;
1029   APICWrite(APIC_SIVR, tmp);
1030 }
1031
1032
1033 inline ULONG ThisCPU(
1034    VOID)
1035 {
1036    return (APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
1037 }
1038
1039
1040 static VOID APICDumpBit(ULONG base)
1041 {
1042         ULONG v, i, j;
1043
1044         DbgPrint("0123456789abcdef0123456789abcdef\n");
1045         for (i = 0; i < 8; i++) {
1046                 APICRead(base + i*0x10);
1047                 for (j = 0; j < 32; j++) {
1048                         if (v & (1<<j))
1049                                 DbgPrint("1");
1050                         else
1051                                 DbgPrint("0");
1052                 }
1053                 DbgPrint("\n");
1054         }
1055 }
1056
1057
1058 VOID APICDump(VOID)
1059 /*
1060  * Dump the contents of the local APIC registers
1061  */
1062 {
1063   ULONG v, ver, maxlvt;
1064   ULONG r1, r2, w1, w2;
1065
1066   r1 = lastregr;
1067   r2 = lastvalr;
1068   w1 = lastregw;
1069   w2 = lastvalw;
1070
1071         DbgPrint("\nPrinting local APIC contents on CPU(%d):\n", ThisCPU());
1072         v = APICRead(APIC_ID);
1073         DbgPrint("... ID     : %08x (%01x) ", v, GET_APIC_ID(v));
1074         v = APICRead(APIC_VER);
1075         DbgPrint("... VERSION: %08x\n", v);
1076         ver = GET_APIC_VERSION(v);
1077         maxlvt = CPUMap[ThisCPU()].MaxLVT;
1078
1079         v = APICRead(APIC_TPR);
1080         DbgPrint("... TPR    : %08x (%02x)", v, v & ~0);
1081
1082         if (APIC_INTEGRATED(ver)) {                     /* !82489DX */
1083                 v = APICRead(APIC_APR);
1084                 DbgPrint("... APR    : %08x (%02x)\n", v, v & ~0);
1085                 v = APICRead(APIC_PPR);
1086                 DbgPrint("... PPR    : %08x\n", v);
1087         }
1088
1089         v = APICRead(APIC_EOI);
1090         DbgPrint("... EOI    : %08x  !  ", v);
1091         v = APICRead(APIC_LDR);
1092         DbgPrint("... LDR    : %08x\n", v);
1093         v = APICRead(APIC_DFR);
1094         DbgPrint("... DFR    : %08x  !  ", v);
1095         v = APICRead(APIC_SIVR);
1096         DbgPrint("... SIVR   : %08x\n", v);
1097
1098   if (0)
1099   {
1100         DbgPrint("... ISR field:\n");
1101         APICDumpBit(APIC_ISR);
1102         DbgPrint("... TMR field:\n");
1103         APICDumpBit(APIC_TMR);
1104         DbgPrint("... IRR field:\n");
1105         APICDumpBit(APIC_IRR);
1106   }
1107
1108         if (APIC_INTEGRATED(ver)) {             /* !82489DX */
1109                 if (maxlvt > 3)         /* Due to the Pentium erratum 3AP. */
1110                         APICWrite(APIC_ESR, 0);
1111                 v = APICRead(APIC_ESR);
1112                 DbgPrint("... ESR    : %08x\n", v);
1113         }
1114
1115         v = APICRead(APIC_ICR0);
1116         DbgPrint("... ICR0   : %08x  !  ", v);
1117         v = APICRead(APIC_ICR1);
1118         DbgPrint("... ICR1   : %08x  !  ", v);
1119
1120         v = APICRead(APIC_LVTT);
1121         DbgPrint("... LVTT   : %08x\n", v);
1122
1123         if (maxlvt > 3) {                       /* PC is LVT#4. */
1124                 v = APICRead(APIC_LVTPC);
1125                 DbgPrint("... LVTPC  : %08x  !  ", v);
1126         }
1127         v = APICRead(APIC_LINT0);
1128         DbgPrint("... LINT0  : %08x  !  ", v);
1129         v = APICRead(APIC_LINT1);
1130         DbgPrint("... LINT1  : %08x\n", v);
1131
1132         if (maxlvt > 2) {
1133                 v = APICRead(APIC_LVT3);
1134                 DbgPrint("... LVT3   : %08x\n", v);
1135         }
1136
1137         v = APICRead(APIC_ICRT);
1138         DbgPrint("... ICRT   : %08x  !  ", v);
1139         v = APICRead(APIC_CCRT);
1140         DbgPrint("... CCCT   : %08x  !  ", v);
1141         v = APICRead(APIC_TDCR);
1142         DbgPrint("... TDCR   : %08x\n", v);
1143         DbgPrint("\n");
1144   DbgPrint("Last register read (offset): 0x%08X\n", r1);
1145   DbgPrint("Last register read (value): 0x%08X\n", r2);
1146   DbgPrint("Last register written (offset): 0x%08X\n", w1);
1147   DbgPrint("Last register written (value): 0x%08X\n", w2);
1148   DbgPrint("\n");
1149 }
1150
1151
1152 ULONG Read8254Timer(VOID)
1153 {
1154         ULONG Count;
1155
1156         WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
1157         Count = READ_PORT_UCHAR((PUCHAR)0x40);
1158         Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
1159
1160         return Count;
1161 }
1162
1163
1164 VOID WaitFor8254Wraparound(VOID)
1165 {
1166         ULONG CurCount, PrevCount = ~0;
1167         LONG Delta;
1168
1169         CurCount = Read8254Timer();
1170
1171         do {
1172                 PrevCount = CurCount;
1173                 CurCount = Read8254Timer();
1174                 Delta = CurCount - PrevCount;
1175
1176         /*
1177          * This limit for delta seems arbitrary, but it isn't, it's
1178          * slightly above the level of error a buggy Mercury/Neptune
1179          * chipset timer can cause.
1180          */
1181
1182         } while (Delta < 300);
1183 }
1184
1185 #define HZ (100)
1186 #define APIC_DIVISOR (16)
1187
1188 VOID APICSetupLVTT(
1189    ULONG ClockTicks)
1190 {
1191         ULONG tmp;
1192
1193   /* Periodic timer */
1194         tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
1195     APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;
1196         APICWrite(APIC_LVTT, tmp);
1197
1198   tmp = APICRead(APIC_TDCR);
1199   tmp &= ~(APIC_TDCR_1 | APIC_TDCR_TMBASE | APIC_TDCR_16);
1200         APICWrite(APIC_TDCR, tmp);
1201         APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);
1202 }
1203
1204
1205 VOID APICCalibrateTimer(
1206    ULONG CPU)
1207 {
1208         ULARGE_INTEGER t1, t2;
1209         LONG tt1, tt2;
1210
1211         DPRINT("Calibrating APIC timer...\n");
1212
1213         APICSetupLVTT(~0);
1214
1215         /*
1216          * The timer chip counts down to zero. Let's wait
1217          * for a wraparound to start exact measurement:
1218          * (the current tick might have been already half done)
1219          */
1220         WaitFor8254Wraparound();
1221
1222         /*
1223          * We wrapped around just now. Let's start
1224          */
1225   ReadPentiumClock(&t1);
1226   tt1 = APICRead(APIC_CCRT);
1227
1228         WaitFor8254Wraparound();
1229
1230         tt2 = APICRead(APIC_CCRT);
1231   ReadPentiumClock(&t2);
1232
1233         CPUMap[CPU].BusSpeed = (HZ * (tt2 - tt1) * APIC_DIVISOR);
1234         CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
1235
1236         /* Setup timer for normal operation */
1237   //APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100); // 100ns
1238   APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 15000); // 15ms
1239   //APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
1240
1241   DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
1242           CPUMap[CPU].CoreSpeed/1000000,
1243                 CPUMap[CPU].CoreSpeed%1000000);
1244
1245         DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
1246                 CPUMap[CPU].BusSpeed/1000000,
1247                 CPUMap[CPU].BusSpeed%1000000);
1248 }
1249
1250
1251 static VOID APICSleep(
1252    ULONG Count)
1253 /*
1254    PARAMETERS:
1255       Count = Number of microseconds to busy wait
1256  */
1257 {
1258   KeStallExecutionProcessor(Count);
1259 }
1260
1261
1262 static VOID APICSyncArbIDs(
1263   VOID)
1264 {
1265   ULONG i, tmp;
1266
1267    /* Wait up to 100ms for the APIC to become ready */
1268    for (i = 0; i < 10000; i++) {
1269       tmp = APICRead(APIC_ICR0);
1270       /* Check Delivery Status */
1271       if ((tmp & APIC_ICR0_DS) == 0)
1272          break;
1273       APICSleep(10);
1274    }
1275
1276    if (i == 10000) {
1277       DPRINT("CPU(%d) APIC busy for 100ms.\n", ThisCPU());
1278    }
1279
1280         DPRINT("Synchronizing Arb IDs.\n");
1281         APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);
1282 }
1283
1284
1285 VOID APICSendIPI(
1286    ULONG Target,
1287    ULONG DeliveryMode,
1288    ULONG IntNum,
1289    ULONG Level)
1290 {
1291    ULONG tmp, i, flags;
1292
1293    pushfl(flags);
1294    __asm__ ("\n\tcli\n\t");
1295
1296    /* Wait up to 100ms for the APIC to become ready */
1297    for (i = 0; i < 10000; i++) {
1298       tmp = APICRead(APIC_ICR0);
1299       /* Check Delivery Status */
1300       if ((tmp & APIC_ICR0_DS) == 0)
1301          break;
1302       APICSleep(10);
1303    }
1304
1305    if (i == 10000) {
1306       DPRINT("CPU(%d) Previous IPI was not delivered after 100ms.\n", ThisCPU());
1307    }
1308
1309    /* Setup the APIC to deliver the IPI */
1310    tmp = APICRead(APIC_ICR1);
1311    tmp &= 0x00FFFFFF;
1312    APICWrite(APIC_ICR1, tmp | SET_APIC_DEST_FIELD(Target));
1313
1314    tmp  = APICRead(APIC_ICR0);
1315    tmp &= ~(APIC_ICR0_LEVEL | APIC_ICR0_DESTM | APIC_ICR0_DM | APIC_ICR0_VECTOR);
1316    tmp |= (DeliveryMode | IntNum | Level);
1317
1318    if (Target == APIC_TARGET_SELF) {
1319       tmp |= APIC_ICR0_DESTS_SELF;
1320    } else if (Target == APIC_TARGET_ALL) {
1321       tmp |= APIC_ICR0_DESTS_ALL;
1322    } else if (Target == APIC_TARGET_ALL_BUT_SELF) {
1323       tmp |= APIC_ICR0_DESTS_ALL_BUT_SELF;
1324    } else {
1325       tmp |= APIC_ICR0_DESTS_FIELD;
1326    }
1327
1328    /* Now, fire off the IPI */
1329    APICWrite(APIC_ICR0, tmp);
1330
1331    popfl(flags);
1332 }
1333
1334
1335 BOOLEAN VerifyLocalAPIC(
1336   VOID)
1337 {
1338         UINT reg0, reg1;
1339   
1340   /* The version register is read-only in a real APIC */
1341         reg0 = APICRead(APIC_VER);
1342         APICWrite(APIC_VER, reg0 ^ APIC_VER_MASK);
1343         reg1 = APICRead(APIC_VER);
1344
1345         if (reg1 != reg0)
1346                 return FALSE;
1347
1348         /* The ID register is read/write in a real APIC */
1349         reg0 = APICRead(APIC_ID);
1350         APICWrite(APIC_ID, reg0 ^ APIC_ID_MASK);
1351   reg1 = APICRead(APIC_ID);
1352         APICWrite(APIC_ID, reg0);
1353         if (reg1 != (reg0 ^ APIC_ID_MASK))
1354                 return FALSE;
1355
1356   return TRUE;
1357 }
1358
1359
1360 static VOID SetInterruptGate(
1361   ULONG index,
1362   ULONG address)
1363 {
1364   IDT_DESCRIPTOR *idt;
1365
1366   idt = (IDT_DESCRIPTOR*)((ULONG)KeGetCurrentKPCR()->IDT + index * sizeof(IDT_DESCRIPTOR));
1367   idt->a = (((ULONG)address)&0xffff) + (KERNEL_CS << 16);
1368   idt->b = 0x8f00 + (((ULONG)address)&0xffff0000);
1369 }
1370
1371
1372 VOID MpsTimerHandler(
1373   VOID)
1374 {
1375 #if 0
1376   KIRQL OldIrql;
1377 #endif
1378
1379   DPRINT("T1");
1380
1381   /*
1382    * Acknowledge the interrupt
1383    */
1384   APICSendEOI();
1385 #if 0
1386   /*
1387    * Notify the rest of the kernel of the raised irq level
1388    */
1389   OldIrql = KeRaiseIrqlToSynchLevel();
1390 #endif
1391   __asm__("sti\n\t");
1392
1393   /*
1394    * Call the dispatcher
1395    */
1396   PsDispatchThread(THREAD_STATE_RUNNABLE);
1397
1398 #if 0
1399   /*
1400    * Lower irq level
1401    */
1402   KeLowerIrql(OldIrql);
1403 #endif
1404 }
1405
1406
1407 VOID MpsErrorHandler(
1408   VOID)
1409 {
1410   ULONG tmp1, tmp2;
1411
1412   APICDump();
1413
1414   tmp1 = APICRead(APIC_ESR);
1415         APICWrite(APIC_ESR, 0);
1416         tmp2 = APICRead(APIC_ESR);
1417
1418   /*
1419    * Acknowledge the interrupt
1420    */
1421   APICSendEOI();
1422
1423         /* Here is what the APIC error bits mean:
1424            0: Send CS error
1425            1: Receive CS error
1426            2: Send accept error
1427            3: Receive accept error
1428            4: Reserved
1429            5: Send illegal vector
1430            6: Received illegal vector
1431            7: Illegal register address
1432         */
1433         DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
1434   for (;;);
1435 }
1436
1437
1438 VOID MpsSpuriousHandler(
1439   VOID)
1440 {
1441   DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
1442
1443         /*
1444    * Acknowledge the interrupt
1445    */
1446   APICSendEOI();
1447   APICDump();
1448   for (;;);
1449 }
1450
1451
1452 VOID APICSetup(
1453   VOID)
1454 {
1455   ULONG CPU, tmp;
1456
1457   CPU = ThisCPU();
1458
1459   /*
1460          * Intel recommends to set DFR, LDR and TPR before enabling
1461          * an APIC.  See e.g. "AP-388 82489DX User's Manual" (Intel
1462          * document number 292116).  So here it goes...
1463          */
1464
1465         /*
1466          * Put the APIC into flat delivery mode.
1467          * Must be "all ones" explicitly for 82489DX.
1468          */
1469         APICWrite(APIC_DFR, 0xFFFFFFFF);
1470
1471   /*
1472          * Set up the logical destination ID.
1473          */
1474         tmp = APICRead(APIC_LDR);
1475         tmp &= ~APIC_LDR_MASK;
1476         tmp |= (1 << (CPU + 24));
1477         APICWrite(APIC_LDR, tmp);
1478
1479         /* Accept all interrupts */
1480         tmp = (APICRead(APIC_TPR) & ~APIC_TPR_PRI);
1481         APICWrite(APIC_TPR, tmp);
1482
1483         /* Enable local APIC */
1484         tmp = APICRead(APIC_SIVR) | APIC_SIVR_ENABLE | APIC_SIVR_FOCUS; // No focus processor
1485
1486   /* Set spurious interrupt vector */
1487         tmp |= SPURIOUS_VECTOR;
1488
1489         APICWrite(APIC_SIVR, tmp);
1490
1491   /*
1492    * Only the BP should see the LINT1 NMI signal, obviously.
1493    */
1494   if (CPU == 0)
1495                 tmp = APIC_DM_NMI;
1496         else
1497                 tmp = APIC_DM_NMI | APIC_LVT_MASKED;
1498         if (!APIC_INTEGRATED(CPUMap[CPU].APICVersion)) /* 82489DX */
1499                 tmp |= APIC_LVT_LEVEL_TRIGGER;
1500         APICWrite(APIC_LINT1, tmp);
1501
1502         if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) { /* !82489DX */
1503     if (CPUMap[CPU].MaxLVT > 3) {               /* Due to the Pentium erratum 3AP */
1504                         APICWrite(APIC_ESR, 0);
1505     }
1506
1507     tmp = APICRead(APIC_ESR);
1508     DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
1509
1510     /* Enable sending errors */
1511           tmp = ERROR_VECTOR;
1512           APICWrite(APIC_LVT3, tmp);
1513
1514     /*
1515      * Spec says clear errors after enabling vector
1516      */
1517     if (CPUMap[CPU].MaxLVT > 3)
1518       APICWrite(APIC_ESR, 0);
1519     tmp = APICRead(APIC_ESR);
1520     DPRINT("ESR value after enabling vector: 0x%X\n", tmp);
1521         }
1522 }
1523
1524 VOID
1525 HaliInitBSP(
1526    VOID)
1527 {
1528         PUSHORT ps;
1529
1530         /* Only initialize the BSP once */
1531         if (BSPInitialized)
1532                 return;
1533
1534         BSPInitialized = TRUE;
1535
1536         DPRINT("APIC is mapped at 0x%X\n", APICBase);
1537
1538         if (VerifyLocalAPIC()) {
1539                 DPRINT("APIC found\n");
1540         } else {
1541                 DPRINT1("No APIC found\n");
1542                 KeBugCheck(0);
1543         }
1544
1545   CPUMap[BootCPU].MaxLVT = APICGetMaxLVT();
1546
1547   SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
1548   SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
1549   SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
1550
1551   if (APICMode == amPIC) {
1552     EnableSMPMode();
1553   }
1554
1555   APICSetup();
1556
1557         /* BIOS data segment */
1558         BIOSBase = (PULONG)BIOS_AREA;
1559
1560         /* Area for communicating with the APs */
1561         CommonBase = (PULONG)COMMON_AREA;
1562
1563   /* Copy bootstrap code to common area */
1564         memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
1565                     &APstart,
1566                     (ULONG)&APend - (ULONG)&APstart + 1);
1567
1568         /* Set shutdown code */
1569         CMOS_WRITE(0xF, 0xA);
1570
1571         /* Set warm reset vector */
1572         ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
1573         *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
1574
1575         ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
1576         *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
1577
1578   /* Calibrate APIC timer */
1579         APICCalibrateTimer(0);
1580
1581   /* The boot processor is online */
1582   OnlineCPUs = (1 << 0);
1583 }
1584
1585 #endif /* MP */
1586
1587 VOID
1588 STDCALL
1589 HalInitializeProcessor (
1590   ULONG ProcessorNumber,
1591   PVOID ProcessorStack)
1592 {
1593
1594 #ifdef MP
1595
1596    PCOMMON_AREA_INFO Common;
1597    ULONG StartupCount;
1598    ULONG DeliveryStatus;
1599    ULONG AcceptStatus;
1600          ULONG CPU, i, j;
1601          ULONG tmp, maxlvt;
1602
1603    if (ProcessorNumber == 0) {
1604        /* Boot processor is already initialized */
1605        NextCPU = 1;
1606        return;
1607    }
1608
1609    if (NextCPU < CPUCount) {
1610       CPU = NextCPU;
1611
1612       DPRINT("Attempting to boot CPU %d\n", CPU);
1613
1614             /* Send INIT IPI */
1615             APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_ASSERT);
1616
1617             APICSleep(200);
1618
1619             /* Deassert INIT */
1620       APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_DEASSERT);
1621
1622       if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) {
1623                            /* Clear APIC errors */
1624          APICWrite(APIC_ESR, 0);
1625          tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1626       }
1627
1628       Common = (PCOMMON_AREA_INFO)CommonBase;
1629
1630       /* Write the location of the AP stack */
1631       Common->Stack = (ULONG)ProcessorStack;
1632
1633       DPRINT("CPU %d got stack at 0x%X\n", CPU, Common->Stack);
1634 #if 0
1635       for (j = 0; j < 16; j++) {
1636          Common->Debug[j] = 0;
1637       }
1638 #endif
1639
1640       maxlvt = APICGetMaxLVT();
1641
1642                   /* Is this a local APIC or an 82489DX? */
1643       StartupCount = (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) ? 2 : 0;
1644
1645                   for (i = 1; i <= StartupCount; i++)
1646                   {
1647          /* It's a local APIC, so send STARTUP IPI */
1648          DPRINT("Sending startup signal %d\n", i);
1649          /* Clear errors */
1650          APICWrite(APIC_ESR, 0);
1651          APICRead(APIC_ESR);
1652
1653          APICSendIPI(CPUMap[CPU].APICId,
1654             0,
1655             APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12),
1656             APIC_ICR0_LEVEL_DEASSERT);
1657
1658          /* Wait up to 10ms for IPI to be delivered */
1659          j = 0;
1660          do {
1661             APICSleep(10);
1662
1663             /* Check Delivery Status */
1664             DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1665
1666             j++;
1667          } while ((DeliveryStatus) && (j < 1000));
1668
1669          APICSleep(200);
1670
1671                      /*
1672                       * Due to the Pentium erratum 3AP.
1673                       */
1674                      if (maxlvt > 3) {
1675                              APICRead(APIC_SIVR);
1676                              APICWrite(APIC_ESR, 0);
1677                      }
1678
1679          AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1680
1681          if (DeliveryStatus || AcceptStatus) {
1682             break;
1683          }
1684       }
1685
1686       if (DeliveryStatus) {
1687          DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU);
1688       }
1689
1690       if (AcceptStatus) {
1691          DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU);
1692       }
1693
1694       if (!(DeliveryStatus || AcceptStatus)) {
1695
1696          /* Wait no more than 5 seconds for processor to boot */
1697          DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU);
1698
1699          /* Wait no more than 5 seconds */
1700          for (j = 0; j < 50000; j++) {
1701
1702             if (CPUMap[CPU].Flags & CPU_ENABLED)
1703                break;
1704
1705             APICSleep(100);
1706          }
1707       }
1708
1709       if (CPUMap[CPU].Flags & CPU_ENABLED) {
1710          DbgPrint("CPU %d is now running\n", CPU);
1711       } else {
1712          DbgPrint("Initialization of CPU %d failed\n", CPU);
1713       }
1714
1715 #if 0
1716       DPRINT("Debug bytes are:\n");
1717
1718       for (j = 0; j < 4; j++) {
1719          DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1720             Common->Debug[j*4+0],
1721             Common->Debug[j*4+1],
1722             Common->Debug[j*4+2],
1723             Common->Debug[j*4+3]);
1724       }
1725 #endif
1726       NextCPU++;
1727    }
1728
1729 #endif /* MP */
1730
1731 }
1732
1733 BOOLEAN
1734 STDCALL
1735 HalAllProcessorsStarted (
1736         VOID
1737         )
1738 {
1739
1740 #ifdef MP
1741
1742   return (NextCPU >= CPUCount);
1743
1744 #else /* MP */
1745
1746         if (BSPInitialized) {
1747                 return TRUE;
1748         } else {
1749                 BSPInitialized = TRUE;
1750                 return FALSE;
1751         }
1752
1753 #endif /* MP */
1754
1755 }
1756
1757 BOOLEAN
1758 STDCALL
1759 HalStartNextProcessor (
1760         ULONG   Unknown1,
1761         ULONG   Unknown2
1762         )
1763 {
1764 #ifdef MP
1765
1766   /* Display the APIC registers for debugging */
1767   switch (Unknown1) {
1768   case 0:
1769     APICDump();
1770     break;
1771   case 1:
1772     IOAPICDump();
1773   }
1774   for(;;);
1775
1776   return (NextCPU >= CPUCount);
1777
1778 #endif /* MP */
1779
1780   return FALSE;
1781 }
1782
1783
1784 #ifdef MP
1785
1786 ULONG MPChecksum(
1787    PUCHAR Base,
1788    ULONG Size)
1789 /*
1790  *      Checksum an MP configuration block
1791  */
1792 {
1793    ULONG Sum = 0;
1794
1795    while (Size--)
1796       Sum += *Base++;
1797
1798    return((UCHAR)Sum);
1799 }
1800
1801
1802 PCHAR HaliMPFamily(
1803    ULONG Family,
1804    ULONG Model)
1805 {
1806    static CHAR str[32];
1807    static PCHAR CPUs[] =
1808    {
1809       "80486DX", "80486DX",
1810       "80486SX", "80486DX/2 or 80487",
1811       "80486SL", "Intel5X2(tm)",
1812       "Unknown", "Unknown",
1813       "80486DX/4"
1814    };
1815    if (Family == 0x6)
1816       return ("Pentium(tm) Pro");
1817    if (Family == 0x5)
1818       return ("Pentium(tm)");
1819    if (Family == 0x0F && Model == 0x0F)
1820       return("Special controller");
1821    if (Family == 0x0F && Model == 0x00)
1822       return("Pentium 4(tm)");
1823    if (Family == 0x04 && Model < 9)
1824       return CPUs[Model];
1825    sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
1826    return str;
1827 }
1828
1829
1830 static VOID HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
1831 {
1832   ULONG ver;
1833
1834   if (!(m->CpuFlags & CPU_FLAG_ENABLED))
1835     return;
1836
1837   DPRINT("Processor #%d %s APIC version %d\n",
1838     m->ApicId,
1839     HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
1840       (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
1841       m->ApicVersion);
1842
1843   if (m->FeatureFlags & (1 << 0))
1844     DPRINT("    Floating point unit present.\n");
1845   if (m->FeatureFlags & (1 << 7))
1846     DPRINT("    Machine Exception supported.\n");
1847   if (m->FeatureFlags & (1 << 8))
1848     DPRINT("    64 bit compare & exchange supported.\n");
1849   if (m->FeatureFlags & (1 << 9))
1850     DPRINT("    Internal APIC present.\n");
1851   if (m->FeatureFlags & (1 << 11))
1852     DPRINT("    SEP present.\n");
1853   if (m->FeatureFlags & (1 << 12))
1854     DPRINT("    MTRR present.\n");
1855   if (m->FeatureFlags & (1 << 13))
1856     DPRINT("    PGE  present.\n");
1857   if (m->FeatureFlags & (1 << 14))
1858     DPRINT("    MCA  present.\n");
1859   if (m->FeatureFlags & (1 << 15))
1860     DPRINT("    CMOV  present.\n");
1861   if (m->FeatureFlags & (1 << 16))
1862     DPRINT("    PAT  present.\n");
1863   if (m->FeatureFlags & (1 << 17))
1864     DPRINT("    PSE  present.\n");
1865   if (m->FeatureFlags & (1 << 18))
1866     DPRINT("    PSN  present.\n");
1867   if (m->FeatureFlags & (1 << 19))
1868     DPRINT("    Cache Line Flush Instruction present.\n");
1869   /* 20 Reserved */
1870   if (m->FeatureFlags & (1 << 21))
1871     DPRINT("    Debug Trace and EMON Store present.\n");
1872   if (m->FeatureFlags & (1 << 22))
1873     DPRINT("    ACPI Thermal Throttle Registers present.\n");
1874   if (m->FeatureFlags & (1 << 23))
1875     DPRINT("    MMX  present.\n");
1876   if (m->FeatureFlags & (1 << 24))
1877     DPRINT("    FXSR  present.\n");
1878   if (m->FeatureFlags & (1 << 25))
1879     DPRINT("    XMM  present.\n");
1880   if (m->FeatureFlags & (1 << 26))
1881     DPRINT("    Willamette New Instructions present.\n");
1882   if (m->FeatureFlags & (1 << 27))
1883     DPRINT("    Self Snoop present.\n");
1884   /* 28 Reserved */
1885   if (m->FeatureFlags & (1 << 29))
1886     DPRINT("    Thermal Monitor present.\n");
1887   /* 30, 31 Reserved */
1888
1889   CPUMap[CPUCount].APICId = m->ApicId;
1890
1891   CPUMap[CPUCount].Flags = CPU_USABLE;
1892
1893   if (m->CpuFlags & CPU_FLAG_BSP) {
1894     DPRINT("    Bootup CPU\n");
1895     CPUMap[CPUCount].Flags |= CPU_BSP;
1896     BootCPU = m->ApicId;
1897   }
1898
1899   if (m->ApicId > MAX_CPU) {
1900     DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
1901     return;
1902   }
1903   ver = m->ApicVersion;
1904
1905   /*
1906   * Validate version
1907   */
1908   if (ver == 0x0) {
1909     DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
1910     ver = 0x10;
1911   }
1912   CPUMap[CPUCount].APICVersion = ver;
1913   
1914   CPUCount++;
1915 }
1916
1917 static VOID HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
1918 {
1919   static ULONG CurrentPCIBusId = 0;
1920         CHAR str[7];
1921
1922         memcpy(str, m->BusType, 6);
1923         str[6] = 0;
1924         DPRINT("Bus #%d is %s\n", m->BusId, str);
1925
1926         if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) {
1927                 BUSMap[m->BusId] = MP_BUS_ISA;
1928         } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) {
1929                 BUSMap[m->BusId] = MP_BUS_EISA;
1930         } else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) {
1931                 BUSMap[m->BusId] = MP_BUS_PCI;
1932                 PCIBUSMap[m->BusId] = CurrentPCIBusId;
1933                 CurrentPCIBusId++;
1934         } else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
1935                 BUSMap[m->BusId] = MP_BUS_MCA;
1936         } else {
1937                 DPRINT("Unknown bustype %s - ignoring\n", str);
1938         }
1939 }
1940
1941 static VOID HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
1942 {
1943   if (!(m->ApicFlags & CPU_FLAG_ENABLED))
1944     return;
1945
1946   DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
1947     m->ApicId, m->ApicVersion, m->ApicAddress);
1948   if (IOAPICCount > MAX_IOAPIC) {
1949     DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
1950       MAX_IOAPIC, IOAPICCount);
1951     DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
1952     KeBugCheck(0);
1953   }
1954   IOAPICMap[IOAPICCount].ApicId = m->ApicId;
1955   IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
1956   IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
1957   IOAPICCount++;
1958 }
1959
1960 static VOID HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
1961 {
1962   DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
1963     " IRQ %02x, APIC ID %x, APIC INT %02x\n",
1964     m->IrqType, m->IrqFlag & 3,
1965     (m->IrqFlag >> 2) & 3, m->SrcBusId,
1966     m->SrcBusIrq, m->DstApicId, m->DstApicInt);
1967   if (IRQCount > MAX_IRQ_SOURCE) {
1968     DPRINT1("Max # of irq sources exceeded!!\n");
1969     KeBugCheck(0);
1970   }
1971
1972   IRQMap[IRQCount] = *m;
1973   IRQCount++;
1974 }
1975
1976 static VOID HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
1977 {
1978   DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
1979     " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
1980     m->IrqType, m->IrqFlag & 3,
1981     (m->IrqFlag >> 2) & 3, m->SrcBusId,
1982     m->SrcBusIrq, m->DstApicId, m->DstApicLInt);
1983   /*
1984    * Well it seems all SMP boards in existence
1985    * use ExtINT/LVT1 == LINT0 and
1986    * NMI/LVT2 == LINT1 - the following check
1987    * will show us if this assumptions is false.
1988    * Until then we do not have to add baggage.
1989    */
1990   if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0)) {
1991     DPRINT1("Invalid MP table!\n");
1992     KeBugCheck(0);
1993   }
1994   if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1)) {
1995     DPRINT1("Invalid MP table!\n");
1996     KeBugCheck(0);
1997   }
1998 }
1999
2000
2001 VOID
2002 HaliReadMPConfigTable(
2003    PMP_CONFIGURATION_TABLE Table)
2004 /*
2005    PARAMETERS:
2006       Table = Pointer to MP configuration table
2007  */
2008 {
2009    PUCHAR Entry;
2010    ULONG Count;
2011
2012    if (Table->Signature != MPC_SIGNATURE)
2013      {
2014        PUCHAR pc = (PUCHAR)&Table->Signature;
2015        
2016        DbgPrint("Bad MP configuration block signature: %c%c%c%c\n", 
2017                 pc[0], pc[1], pc[2], pc[3]);
2018        KeBugCheck(0);
2019        return;
2020      }
2021
2022    if (MPChecksum((PUCHAR)Table, Table->Length))
2023      {
2024        DbgPrint("Bad MP configuration block checksum\n");
2025        KeBugCheck(0);
2026        return;
2027      }
2028
2029    if (Table->Specification < 0x04)
2030      {
2031        DbgPrint("Bad MP configuration table version (%d)\n",
2032                 Table->Specification);
2033        KeBugCheck(0);
2034        return;
2035      }
2036
2037    APICBase = (PULONG)Table->LocalAPICAddress;
2038    if (APICBase != (PULONG)APIC_DEFAULT_BASE)
2039      {
2040        DbgPrint("APIC base address is at 0x%X. " \
2041                 "I cannot handle non-standard adresses\n", APICBase);
2042        KeBugCheck(0);
2043      }
2044
2045    Entry = (PUCHAR)((PVOID)Table + sizeof(MP_CONFIGURATION_TABLE));
2046    Count = 0;
2047    while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
2048    {
2049      /* Switch on type */
2050      switch (*Entry)
2051        {
2052        case MPCTE_PROCESSOR:
2053          {
2054            HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
2055            Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
2056            Count += sizeof(MP_CONFIGURATION_PROCESSOR);
2057            break;
2058          }
2059        case MPCTE_BUS:
2060          {
2061            HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
2062            Entry += sizeof(MP_CONFIGURATION_BUS);
2063            Count += sizeof(MP_CONFIGURATION_BUS);
2064            break;
2065          }
2066        case MPCTE_IOAPIC:
2067          {
2068            HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
2069            Entry += sizeof(MP_CONFIGURATION_IOAPIC);
2070            Count += sizeof(MP_CONFIGURATION_IOAPIC);
2071            break;
2072          }
2073        case MPCTE_INTSRC:
2074          {
2075            HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
2076            Entry += sizeof(MP_CONFIGURATION_INTSRC);
2077            Count += sizeof(MP_CONFIGURATION_INTSRC);
2078            break;
2079          }
2080        case MPCTE_LINTSRC:
2081          {
2082            HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
2083            Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
2084            Count += sizeof(MP_CONFIGURATION_INTLOCAL);
2085            break;
2086          }
2087        default:
2088          DbgPrint("Unknown entry in MPC table\n");
2089          KeBugCheck(0);
2090        }
2091    }
2092 }
2093
2094
2095 static VOID HaliConstructDefaultIOIrqMPTable(
2096   ULONG Type)
2097 {
2098         MP_CONFIGURATION_INTSRC intsrc;
2099         ULONG i;
2100
2101         intsrc.Type = MPCTE_INTSRC;
2102         intsrc.IrqFlag = 0;                     /* conforming */
2103         intsrc.SrcBusId = 0;
2104         intsrc.DstApicId = IOAPICMap[0].ApicId;
2105
2106         intsrc.IrqType = INT_VECTORED;
2107         for (i = 0; i < 16; i++) {
2108                 switch (Type) {
2109                 case 2:
2110                         if (i == 0 || i == 13)
2111                                 continue;       /* IRQ0 & IRQ13 not connected */
2112                         /* Fall through */
2113                 default:
2114                         if (i == 2)
2115                                 continue;       /* IRQ2 is never connected */
2116                 }
2117
2118                 intsrc.SrcBusIrq = i;
2119                 intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
2120                 HaliMPIntSrcInfo(&intsrc);
2121         }
2122
2123         intsrc.IrqType = INT_EXTINT;
2124         intsrc.SrcBusIrq = 0;
2125         intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
2126         HaliMPIntSrcInfo(&intsrc);
2127 }
2128
2129
2130 static VOID HaliConstructDefaultISAMPTable(
2131   ULONG Type)
2132 {
2133         MP_CONFIGURATION_PROCESSOR processor;
2134         MP_CONFIGURATION_BUS bus;
2135         MP_CONFIGURATION_IOAPIC ioapic;
2136         MP_CONFIGURATION_INTLOCAL lintsrc;
2137         ULONG linttypes[2] = { INT_EXTINT, INT_NMI };
2138         ULONG i;
2139
2140   APICBase = (PULONG)APIC_DEFAULT_BASE;
2141
2142   /*
2143    * 2 CPUs, numbered 0 & 1.
2144    */
2145   processor.Type = MPCTE_PROCESSOR;
2146         /* Either an integrated APIC or a discrete 82489DX. */
2147   processor.ApicVersion = Type > 4 ? 0x10 : 0x01;
2148   processor.CpuFlags = CPU_FLAG_ENABLED | CPU_FLAG_BSP;
2149   /* FIXME: Get this from the bootstrap processor */
2150   processor.CpuSignature = 0;
2151   processor.FeatureFlags = 0;
2152   processor.Reserved[0] = 0;
2153   processor.Reserved[1] = 0;
2154   for (i = 0; i < 2; i++) {
2155     processor.ApicId = i;
2156     HaliMPProcessorInfo(&processor);
2157     processor.CpuFlags &= ~CPU_FLAG_BSP;
2158   }
2159
2160   bus.Type = MPCTE_BUS;
2161   bus.BusId = 0;
2162   switch (Type) {
2163     default:
2164     DPRINT("Unknown standard configuration %d\n", Type);
2165       /* Fall through */
2166     case 1:
2167     case 5:
2168       memcpy(bus.BusType, "ISA   ", 6);
2169       break;
2170     case 2:
2171     case 6:
2172     case 3:
2173       memcpy(bus.BusType, "EISA  ", 6);
2174       break;
2175     case 4:
2176     case 7:
2177       memcpy(bus.BusType, "MCA   ", 6);
2178   }
2179   HaliMPBusInfo(&bus);
2180   if (Type > 4) {
2181     bus.Type = MPCTE_BUS;
2182     bus.BusId = 1;
2183     memcpy(bus.BusType, "PCI   ", 6);
2184     HaliMPBusInfo(&bus);
2185   }
2186
2187   ioapic.Type = MPCTE_IOAPIC;
2188   ioapic.ApicId = 2;
2189   ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
2190   ioapic.ApicFlags = MP_IOAPIC_USABLE;
2191   ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
2192   HaliMPIOApicInfo(&ioapic);
2193
2194   /*
2195    * We set up most of the low 16 IO-APIC pins according to MPS rules.
2196    */
2197   HaliConstructDefaultIOIrqMPTable(Type);
2198
2199   lintsrc.Type = MPCTE_LINTSRC;
2200   lintsrc.IrqType = 0;
2201   lintsrc.IrqFlag = 0;  /* conforming */
2202   lintsrc.SrcBusId = 0;
2203   lintsrc.SrcBusIrq = 0;
2204   lintsrc.DstApicId = MP_APIC_ALL;
2205   for (i = 0; i < 2; i++) {
2206     lintsrc.IrqType = linttypes[i];
2207     lintsrc.DstApicLInt = i;
2208     HaliMPIntLocalInfo(&lintsrc);
2209   }
2210 }
2211
2212 BOOLEAN
2213 HaliScanForMPConfigTable(
2214    ULONG Base,
2215    ULONG Size)
2216 /*
2217    PARAMETERS:
2218       Base = Base address of region
2219       Size = Length of region to check
2220    RETURNS:
2221       TRUE if a valid MP configuration table was found
2222  */
2223 {
2224          PULONG bp = (PULONG)Base;
2225          MP_FLOATING_POINTER* mpf;
2226
2227    while (Size > 0)
2228    {
2229       if (*bp == MPF_SIGNATURE)
2230       {
2231         DbgPrint("Found MPF signature at %x, checksum %x\n", bp,
2232                  MPChecksum((PUCHAR)bp, 16));
2233          if (MPChecksum((PUCHAR)bp, 16) == 0)
2234          {
2235             mpf = (MP_FLOATING_POINTER*)bp;
2236
2237             DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
2238               mpf->Specification);
2239
2240             if (mpf->Feature2 & FEATURE2_IMCRP) {
2241                APICMode = amPIC;
2242                DPRINT("Running in IMCR and PIC compatibility mode.\n")
2243             } else {
2244                APICMode = amVWIRE;
2245                DPRINT("Running in Virtual Wire compatibility mode.\n");
2246                                     }
2247
2248             switch (mpf->Feature1)
2249             {
2250                case 0:
2251                   /* Non standard configuration */
2252                   break;
2253                case 1:
2254                   DPRINT("ISA\n");
2255                   break;
2256                case 2:
2257                   DPRINT("EISA with no IRQ8 chaining\n");
2258                   break;
2259                case 3:
2260                   DPRINT("EISA\n");
2261                   break;
2262                case 4:
2263                   DPRINT("MCA\n");
2264                   break;
2265                case 5:
2266                   DPRINT("ISA and PCI\n");
2267                   break;
2268                case 6:
2269                   DPRINT("EISA and PCI\n");
2270                   break;
2271                case 7:
2272                   DPRINT("MCA and PCI\n");
2273                   break;
2274                default:
2275                   DbgPrint("Unknown standard configuration %d\n", mpf->Feature1);
2276                   return FALSE;
2277             }
2278
2279             CPUCount = 0;
2280             IOAPICCount = 0;
2281             IRQCount = 0;
2282
2283             if ((mpf->Feature1 == 0) && (mpf->Address)) {
2284               HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)mpf->Address);
2285             } else {
2286               HaliConstructDefaultISAMPTable(mpf->Feature1);
2287             }
2288
2289             return TRUE;
2290          }
2291       }
2292       bp += 4;
2293       Size -= 16;
2294    }
2295    return FALSE;
2296 }
2297
2298
2299 VOID
2300 HalpInitMPS(
2301    VOID)
2302 {
2303    USHORT EBDA;
2304    ULONG CPU;
2305
2306    /* Only initialize MP system once. Once called the first time,
2307       each subsequent call is part of the initialization sequence
2308                         for an application processor. */
2309   if (MPSInitialized) {
2310     CPU = ThisCPU();
2311
2312     DPRINT("CPU %d says it is now booted.\n", CPU);
2313
2314     APICSetup();
2315     APICCalibrateTimer(CPU);
2316
2317     /* This processor is now booted */
2318     CPUMap[CPU].Flags |= CPU_ENABLED;
2319     OnlineCPUs |= (1 << CPU);
2320
2321     return;
2322   }
2323
2324   MPSInitialized = TRUE;
2325
2326   /*
2327      Scan the system memory for an MP configuration table
2328        1) Scan the first KB of system base memory
2329        2) Scan the last KB of system base memory
2330        3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
2331        4) Scan the Extended BIOS Data Area
2332    */
2333
2334   if (!HaliScanForMPConfigTable(0x0, 0x400)) {
2335     if (!HaliScanForMPConfigTable(0x9FC00, 0x400)) {
2336       if (!HaliScanForMPConfigTable(0xF0000, 0x10000)) {
2337         EBDA = *((PUSHORT)0x040E);
2338         EBDA <<= 4;
2339         if (!HaliScanForMPConfigTable((ULONG)EBDA, 0x1000)) {
2340           DbgPrint("No multiprocessor compliant system found.\n");
2341           KeBugCheck(0);
2342         }
2343       }
2344     }
2345   }
2346
2347   /* Setup IRQ to vector translation map */
2348   memset(&IRQVectorMap, sizeof(IRQVectorMap), 0);
2349
2350   /* Initialize the bootstrap processor */
2351   HaliInitBSP();
2352
2353   /* Setup I/O APIC */
2354   IOAPICSetup();
2355
2356   /* Setup busy waiting */
2357   HalpCalibrateStallExecution();
2358
2359   /* We can now enable interrupts */
2360   __asm__ __volatile__ ("sti\n\t");
2361
2362   NextCPU = 0;
2363 }
2364
2365 #endif /* MP */
2366
2367 /* EOF */