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
11 * 22/05/1998 DW Created
12 * 12/04/2001 CSH Added MultiProcessor specification support
15 /* INCLUDES *****************************************************************/
17 #include <ddk/ntddk.h>
21 #include <internal/debug.h>
28 #include <internal/ntoskrnl.h>
29 #include <internal/i386/segment.h>
30 #include <internal/ke.h>
31 #include <internal/ps.h>
34 Address of area to be used for communication between Application
35 Processors (APs) and the BootStrap Processor (BSP)
37 #define COMMON_AREA 0x2000
41 typedef struct __attribute__((packed)) _COMMON_AREA_INFO
43 ULONG Stack; /* Location of AP stack */
44 ULONG Debug[16]; /* For debugging */
45 } COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
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 */
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 */
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 */
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 */
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 */
68 extern CHAR *APstart, *APend;
69 extern VOID (*APflush)(VOID);
71 extern VOID MpsTimerInterrupt(VOID);
72 extern VOID MpsErrorInterrupt(VOID);
73 extern VOID MpsSpuriousInterrupt(VOID);
75 #define CMOS_READ(address) ({ \
76 WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
77 READ_PORT_UCHAR((PUCHAR)0x71)); \
80 #define CMOS_WRITE(address, value) ({ \
81 WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
82 WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
85 BOOLEAN MPSInitialized = FALSE; /* Is the MP system initialized? */
87 VOID APICDisable(VOID);
88 static VOID APICSyncArbIDs(VOID);
99 BOOLEAN BSPInitialized = FALSE; /* Is the BSP initialized? */
102 /* FUNCTIONS *****************************************************************/
106 /* Functions for handling 8259A PICs */
108 VOID Disable8259AIrq(
114 tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
116 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
118 tmp = READ_PORT_UCHAR((PUCHAR)0x21);
120 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
131 tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
133 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
135 tmp = READ_PORT_UCHAR((PUCHAR)0x21);
137 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
142 /* Functions for handling I/O APICs */
144 volatile ULONG IOAPICRead(
150 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
153 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN));
164 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
167 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value;
175 IOAPIC_ROUTE_ENTRY Entry;
178 * Disable it in the IO-APIC irq-routing table
180 memset(&Entry, 0, sizeof(Entry));
182 IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0));
183 IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1));
186 static VOID IOAPICClear(
191 for (Pin = 0; Pin < IOAPICMap[Apic].EntryCount; Pin++)
192 IOAPICClearPin(Apic, Pin);
195 static VOID IOAPICClearAll(
200 for (Apic = 0; Apic < IOAPICCount; Apic++)
204 /* This is performance critical and should probably be done in assembler */
209 IOAPIC_ROUTE_ENTRY Entry;
211 *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
213 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
217 /* This is performance critical and should probably be done in assembler */
218 VOID IOAPICUnmaskIrq(
222 IOAPIC_ROUTE_ENTRY Entry;
224 *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
226 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
236 * Set the IOAPIC ID to the value stored in the MPC table.
238 for (apic = 0; apic < IOAPICCount; apic++) {
240 /* Read the register 0 value */
241 tmp = IOAPICRead(apic, IOAPIC_ID);
243 old_id = IOAPICMap[apic].ApicId;
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",
250 IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp);
254 * We need to adjust the IRQ routing table
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;
263 * Read the right value from the MPC table and
264 * write it into the ID register.
266 DPRINT("Changing IO-APIC physical APIC ID to %d\n",
267 IOAPICMap[apic].ApicId);
269 tmp &= ~IOAPIC_ID_MASK;
270 tmp |= SET_IOAPIC_ID(IOAPICMap[apic].ApicId);
272 IOAPICWrite(apic, IOAPIC_ID, tmp);
277 tmp = IOAPICRead(apic, 0);
278 if (GET_IOAPIC_ID(tmp) != IOAPICMap[apic].ApicId) {
279 DPRINT1("Could not set I/O APIC ID!\n");
287 * EISA Edge/Level control register, ELCR
289 static ULONG EISA_ELCR(
293 PUCHAR port = (PUCHAR)(0x4d0 + (irq >> 3));
294 return (READ_PORT_UCHAR(port) >> (irq & 7)) & 1;
296 DPRINT("Broken MPtable reports ISA irq %d\n", irq);
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 */
305 #define default_EISA_trigger(idx) (EISA_ELCR(IRQMap[idx].SrcBusIrq))
306 #define default_EISA_polarity(idx) (0)
308 /* ISA interrupts are always polarity zero edge triggered,
309 * when listed as conforming in the MP table. */
311 #define default_ISA_trigger(idx) (0)
312 #define default_ISA_polarity(idx) (0)
314 /* PCI interrupts are always polarity one level triggered,
315 * when listed as conforming in the MP table. */
317 #define default_PCI_trigger(idx) (1)
318 #define default_PCI_polarity(idx) (1)
320 /* MCA interrupts are always polarity zero level triggered,
321 * when listed as conforming in the MP table. */
323 #define default_MCA_trigger(idx) (1)
324 #define default_MCA_polarity(idx) (0)
326 static ULONG IRQPolarity(
329 ULONG bus = IRQMap[idx].SrcBusId;
333 * Determine IRQ line polarity (high active or low active):
335 switch (IRQMap[idx].IrqFlag & 3)
337 case 0: /* conforms, ie. bus-type dependent polarity */
341 case MP_BUS_ISA: /* ISA pin */
343 polarity = default_ISA_polarity(idx);
346 case MP_BUS_EISA: /* EISA pin */
348 polarity = default_EISA_polarity(idx);
351 case MP_BUS_PCI: /* PCI pin */
353 polarity = default_PCI_polarity(idx);
356 case MP_BUS_MCA: /* MCA pin */
358 polarity = default_MCA_polarity(idx);
363 DPRINT("Broken BIOS!!\n");
370 case 1: /* high active */
375 case 2: /* reserved */
377 DPRINT("Broken BIOS!!\n");
381 case 3: /* low active */
386 default: /* invalid */
388 DPRINT("Broken BIOS!!\n");
396 static ULONG IRQTrigger(
399 ULONG bus = IRQMap[idx].SrcBusId;
403 * Determine IRQ trigger mode (edge or level sensitive):
405 switch ((IRQMap[idx].IrqFlag >> 2) & 3)
407 case 0: /* conforms, ie. bus-type dependent */
411 case MP_BUS_ISA: /* ISA pin */
413 trigger = default_ISA_trigger(idx);
416 case MP_BUS_EISA: /* EISA pin */
418 trigger = default_EISA_trigger(idx);
421 case MP_BUS_PCI: /* PCI pin */
423 trigger = default_PCI_trigger(idx);
426 case MP_BUS_MCA: /* MCA pin */
428 trigger = default_MCA_trigger(idx);
433 DPRINT("Broken BIOS!!\n");
445 case 2: /* reserved */
447 DPRINT("Broken BIOS!!\n");
456 default: /* invalid */
458 DPRINT("Broken BIOS!!\n");
467 static ULONG Pin2Irq(
473 ULONG bus = IRQMap[idx].SrcBusId;
476 * Debugging check, we are in big trouble if this message pops up!
478 if (IRQMap[idx].DstApicInt != pin) {
479 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\n");
484 case MP_BUS_ISA: /* ISA pin */
488 irq = IRQMap[idx].SrcBusIrq;
491 case MP_BUS_PCI: /* PCI pin */
494 * PCI IRQs are mapped in order
498 irq += IOAPICMap[i++].EntryCount;
504 DPRINT("Unknown bus type %d.\n",bus);
515 * Rough estimation of how many shared IRQs there are, can
516 * be changed anytime.
518 #define MAX_PLUS_SHARED_IRQS PIC_IRQS
519 #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + PIC_IRQS)
522 * This is performance-critical, we want to do it O(1)
524 * the indexing order of this array favors 1:1 mappings
525 * between pins and IRQs.
528 static struct irq_pin_list {
529 ULONG apic, pin, next;
530 } irq_2_pin[PIN_MAP_SIZE];
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.
537 static VOID AddPinToIrq(
542 static ULONG first_free_entry = PIC_IRQS;
543 struct irq_pin_list *entry = irq_2_pin + irq;
546 entry = irq_2_pin + entry->next;
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) {
562 * Find the IRQ entry number of a certain pin.
564 static ULONG IOAPICGetIrqEntry(
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)
582 static ULONG AssignIrqVector(
585 static ULONG current_vector = FIRST_DEVICE_VECTOR, vector_offset = 0;
588 /* There may already have been assigned a vector for this IRQ */
589 vector = IRQVectorMap[irq];
594 if (current_vector > FIRST_SYSTEM_VECTOR) {
596 current_vector = FIRST_DEVICE_VECTOR + vector_offset;
597 } else if (current_vector == FIRST_SYSTEM_VECTOR) {
598 DPRINT1("Ran out of interrupt sources!");
602 IRQVectorMap[irq] = current_vector;
603 return current_vector;
607 VOID IOAPICSetupIrqs(
610 IOAPIC_ROUTE_ENTRY entry;
611 ULONG apic, pin, idx, irq, first_notcon = 1, vector;
613 DPRINT("Init IO_APIC IRQs\n");
615 for (apic = 0; apic < IOAPICCount; apic++) {
616 for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++) {
619 * add it to the IO-APIC irq-routing table
621 memset(&entry,0,sizeof(entry));
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;
628 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED);
631 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap[apic].ApicId, pin);
634 DPRINT(", %d-%d\n", IOAPICMap[apic].ApicId, pin);
639 entry.trigger = IRQTrigger(idx);
640 entry.polarity = IRQPolarity(idx);
645 entry.dest.logical.logical_dest = OnlineCPUs;
648 irq = Pin2Irq(idx, apic, pin);
649 AddPinToIrq(irq, apic, pin);
651 vector = AssignIrqVector(irq);
652 entry.vector = vector;
660 if ((apic == 0) && (irq < 16))
661 Disable8259AIrq(irq);
663 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1));
664 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0));
670 static VOID IOAPICEnable(
675 for (i = 0; i < PIN_MAP_SIZE; i++) {
676 irq_2_pin[i].pin = -1;
677 irq_2_pin[i].next = 0;
681 * The number of IO-APIC IRQ registers (== #pins):
683 for (i = 0; i < IOAPICCount; i++) {
684 tmp = IOAPICRead(i, IOAPIC_VER);
685 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1;
689 * Do not trust the IO-APIC being empty at bootup
695 static VOID IOAPICDisable(
699 * Clear the IO-APIC before rebooting
708 static VOID IOAPICSetup(
714 // FIXME: This causes application processors to not boot if asked to
721 VOID IOAPICDump(VOID)
724 ULONG reg0, reg1, reg2;
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",
730 IOAPICMap[i].EntryCount);
734 * We are a bit conservative about what we expect. We have to
735 * know about every hardware change ASAP.
737 DbgPrint("Testing the IO APIC.......................\n");
739 for (apic = 0; apic < IOAPICCount; apic++) {
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);
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");
755 DbgPrint(".... register #01: %08X\n", reg1);
756 i = GET_IOAPIC_MRE(reg1);
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 */
766 DbgPrint(" WARNING: Unexpected IO-APIC\n");
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");
778 if (reg1 & 0xFF00FF00) {
779 DbgPrint(" WARNING: Unexpected IO-APIC\n");
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");
791 DbgPrint(".... IRQ redirection table:\n");
792 DbgPrint(" NR Log Phy Mask Trig IRR Pol"
793 " Stat Dest Deli Vect: \n");
795 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++) {
796 IOAPIC_ROUTE_ENTRY entry;
798 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2);
799 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2);
801 DbgPrint(" %02x %03X %02X ",
803 entry.dest.logical.logical_dest,
804 entry.dest.physical.physical_dest
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
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
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;
824 DbgPrint("IRQ%d ", i);
826 DbgPrint("-> %d", entry->pin);
829 entry = irq_2_pin + entry->next;
838 DbgPrint(".................................... done.\n");
843 /* Functions for handling local APICs */
846 volatile inline ULONG APICRead(
851 p = (PULONG)((ULONG)APICBase + Offset);
855 volatile inline ULONG APICRead(
863 //DPRINT1("R(0x%X)", Offset);
864 p = (PULONG)((ULONG)APICBase + Offset);
866 //DPRINT1("(0x%08X)\n", *p);
873 inline VOID APICWrite(
879 p = (PULONG)((ULONG)APICBase + Offset);
884 inline VOID APICWrite(
893 //DPRINT1("W(0x%X, 0x%08X)\n", Offset, Value);
894 p = (PULONG)((ULONG)APICBase + Offset);
901 inline VOID APICSendEOI(VOID)
906 APICWrite(APIC_EOI, 0);
914 if (CPUMap[ThisCPU()].MaxLVT > 3)
915 APICWrite(APIC_ESR, 0);
916 tmp = APICRead(APIC_ESR);
917 DbgPrint("ESR %08x\n", tmp);
921 ULONG APICGetMaxLVT(VOID)
923 ULONG tmp, ver, maxlvt;
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;
934 static VOID APICClear(
939 maxlvt = CPUMap[ThisCPU()].MaxLVT;
942 * Careful: we have to set masks only first to deassert
943 * any level-triggered sources.
945 tmp = APICRead(APIC_LVTT);
946 APICWrite(APIC_LVTT, tmp | APIC_LVT_MASKED);
948 tmp = APICRead(APIC_LINT0);
949 APICWrite(APIC_LINT0, tmp | APIC_LVT_MASKED);
951 tmp = APICRead(APIC_LINT1);
952 APICWrite(APIC_LINT1, tmp | APIC_LVT_MASKED);
955 tmp = APICRead(APIC_LVT3);
956 APICWrite(APIC_LVT3, tmp | APIC_LVT3_MASKED);
960 tmp = APICRead(APIC_LVTPC);
961 APICWrite(APIC_LVTPC, tmp | APIC_LVT_MASKED);
965 * Clean APIC state for other OSs:
967 APICRead(APIC_SIVR); // Dummy read
968 APICWrite(APIC_LVTT, APIC_LVT_MASKED);
970 APICRead(APIC_SIVR); // Dummy read
971 APICWrite(APIC_LINT0, APIC_LVT_MASKED);
973 APICRead(APIC_SIVR); // Dummy read
974 APICWrite(APIC_LINT1, APIC_LVT_MASKED);
977 APICRead(APIC_SIVR); // Dummy read
978 APICWrite(APIC_LVT3, APIC_LVT3_MASKED);
982 APICRead(APIC_SIVR); // Dummy read
983 APICWrite(APIC_LVTPC, APIC_LVT_MASKED);
987 /* Enable symetric I/O mode ie. connect the BSP's
988 local APIC to INT and NMI lines */
989 inline VOID EnableSMPMode(
993 * Do not trust the local APIC being empty at bootup.
997 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
998 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x01);
1002 /* Disable symetric I/O mode ie. go to PIC mode */
1003 inline VOID DisableSMPMode(
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.
1012 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
1013 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x00);
1025 * Disable APIC (implies clearing of registers for 82489DX!).
1027 tmp = APICRead(APIC_SIVR);
1028 tmp &= ~APIC_SIVR_ENABLE;
1029 APICWrite(APIC_SIVR, tmp);
1033 inline ULONG ThisCPU(
1036 return (APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
1040 static VOID APICDumpBit(ULONG base)
1044 DbgPrint("0123456789abcdef0123456789abcdef\n");
1045 for (i = 0; i < 8; i++) {
1046 APICRead(base + i*0x10);
1047 for (j = 0; j < 32; j++) {
1060 * Dump the contents of the local APIC registers
1063 ULONG v, ver, maxlvt;
1064 ULONG r1, r2, w1, w2;
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;
1079 v = APICRead(APIC_TPR);
1080 DbgPrint("... TPR : %08x (%02x)", v, v & ~0);
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);
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);
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);
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);
1115 v = APICRead(APIC_ICR0);
1116 DbgPrint("... ICR0 : %08x ! ", v);
1117 v = APICRead(APIC_ICR1);
1118 DbgPrint("... ICR1 : %08x ! ", v);
1120 v = APICRead(APIC_LVTT);
1121 DbgPrint("... LVTT : %08x\n", v);
1123 if (maxlvt > 3) { /* PC is LVT#4. */
1124 v = APICRead(APIC_LVTPC);
1125 DbgPrint("... LVTPC : %08x ! ", v);
1127 v = APICRead(APIC_LINT0);
1128 DbgPrint("... LINT0 : %08x ! ", v);
1129 v = APICRead(APIC_LINT1);
1130 DbgPrint("... LINT1 : %08x\n", v);
1133 v = APICRead(APIC_LVT3);
1134 DbgPrint("... LVT3 : %08x\n", v);
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);
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);
1152 ULONG Read8254Timer(VOID)
1156 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
1157 Count = READ_PORT_UCHAR((PUCHAR)0x40);
1158 Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
1164 VOID WaitFor8254Wraparound(VOID)
1166 ULONG CurCount, PrevCount = ~0;
1169 CurCount = Read8254Timer();
1172 PrevCount = CurCount;
1173 CurCount = Read8254Timer();
1174 Delta = CurCount - PrevCount;
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.
1182 } while (Delta < 300);
1186 #define APIC_DIVISOR (16)
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);
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);
1205 VOID APICCalibrateTimer(
1208 ULARGE_INTEGER t1, t2;
1211 DPRINT("Calibrating APIC timer...\n");
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)
1220 WaitFor8254Wraparound();
1223 * We wrapped around just now. Let's start
1225 ReadPentiumClock(&t1);
1226 tt1 = APICRead(APIC_CCRT);
1228 WaitFor8254Wraparound();
1230 tt2 = APICRead(APIC_CCRT);
1231 ReadPentiumClock(&t2);
1233 CPUMap[CPU].BusSpeed = (HZ * (tt2 - tt1) * APIC_DIVISOR);
1234 CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
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
1241 DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
1242 CPUMap[CPU].CoreSpeed/1000000,
1243 CPUMap[CPU].CoreSpeed%1000000);
1245 DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
1246 CPUMap[CPU].BusSpeed/1000000,
1247 CPUMap[CPU].BusSpeed%1000000);
1251 static VOID APICSleep(
1255 Count = Number of microseconds to busy wait
1258 KeStallExecutionProcessor(Count);
1262 static VOID APICSyncArbIDs(
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)
1277 DPRINT("CPU(%d) APIC busy for 100ms.\n", ThisCPU());
1280 DPRINT("Synchronizing Arb IDs.\n");
1281 APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);
1291 ULONG tmp, i, flags;
1294 __asm__ ("\n\tcli\n\t");
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)
1306 DPRINT("CPU(%d) Previous IPI was not delivered after 100ms.\n", ThisCPU());
1309 /* Setup the APIC to deliver the IPI */
1310 tmp = APICRead(APIC_ICR1);
1312 APICWrite(APIC_ICR1, tmp | SET_APIC_DEST_FIELD(Target));
1314 tmp = APICRead(APIC_ICR0);
1315 tmp &= ~(APIC_ICR0_LEVEL | APIC_ICR0_DESTM | APIC_ICR0_DM | APIC_ICR0_VECTOR);
1316 tmp |= (DeliveryMode | IntNum | Level);
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;
1325 tmp |= APIC_ICR0_DESTS_FIELD;
1328 /* Now, fire off the IPI */
1329 APICWrite(APIC_ICR0, tmp);
1335 BOOLEAN VerifyLocalAPIC(
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);
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))
1360 static VOID SetInterruptGate(
1364 IDT_DESCRIPTOR *idt;
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);
1372 VOID MpsTimerHandler(
1382 * Acknowledge the interrupt
1387 * Notify the rest of the kernel of the raised irq level
1389 OldIrql = KeRaiseIrqlToSynchLevel();
1394 * Call the dispatcher
1396 PsDispatchThread(THREAD_STATE_RUNNABLE);
1402 KeLowerIrql(OldIrql);
1407 VOID MpsErrorHandler(
1414 tmp1 = APICRead(APIC_ESR);
1415 APICWrite(APIC_ESR, 0);
1416 tmp2 = APICRead(APIC_ESR);
1419 * Acknowledge the interrupt
1423 /* Here is what the APIC error bits mean:
1426 2: Send accept error
1427 3: Receive accept error
1429 5: Send illegal vector
1430 6: Received illegal vector
1431 7: Illegal register address
1433 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
1438 VOID MpsSpuriousHandler(
1441 DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
1444 * Acknowledge the interrupt
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...
1466 * Put the APIC into flat delivery mode.
1467 * Must be "all ones" explicitly for 82489DX.
1469 APICWrite(APIC_DFR, 0xFFFFFFFF);
1472 * Set up the logical destination ID.
1474 tmp = APICRead(APIC_LDR);
1475 tmp &= ~APIC_LDR_MASK;
1476 tmp |= (1 << (CPU + 24));
1477 APICWrite(APIC_LDR, tmp);
1479 /* Accept all interrupts */
1480 tmp = (APICRead(APIC_TPR) & ~APIC_TPR_PRI);
1481 APICWrite(APIC_TPR, tmp);
1483 /* Enable local APIC */
1484 tmp = APICRead(APIC_SIVR) | APIC_SIVR_ENABLE | APIC_SIVR_FOCUS; // No focus processor
1486 /* Set spurious interrupt vector */
1487 tmp |= SPURIOUS_VECTOR;
1489 APICWrite(APIC_SIVR, tmp);
1492 * Only the BP should see the LINT1 NMI signal, obviously.
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);
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);
1507 tmp = APICRead(APIC_ESR);
1508 DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
1510 /* Enable sending errors */
1512 APICWrite(APIC_LVT3, tmp);
1515 * Spec says clear errors after enabling vector
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);
1530 /* Only initialize the BSP once */
1534 BSPInitialized = TRUE;
1536 DPRINT("APIC is mapped at 0x%X\n", APICBase);
1538 if (VerifyLocalAPIC()) {
1539 DPRINT("APIC found\n");
1541 DPRINT1("No APIC found\n");
1545 CPUMap[BootCPU].MaxLVT = APICGetMaxLVT();
1547 SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
1548 SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
1549 SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
1551 if (APICMode == amPIC) {
1557 /* BIOS data segment */
1558 BIOSBase = (PULONG)BIOS_AREA;
1560 /* Area for communicating with the APs */
1561 CommonBase = (PULONG)COMMON_AREA;
1563 /* Copy bootstrap code to common area */
1564 memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
1566 (ULONG)&APend - (ULONG)&APstart + 1);
1568 /* Set shutdown code */
1569 CMOS_WRITE(0xF, 0xA);
1571 /* Set warm reset vector */
1572 ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
1573 *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
1575 ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
1576 *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
1578 /* Calibrate APIC timer */
1579 APICCalibrateTimer(0);
1581 /* The boot processor is online */
1582 OnlineCPUs = (1 << 0);
1589 HalInitializeProcessor (
1590 ULONG ProcessorNumber,
1591 PVOID ProcessorStack)
1596 PCOMMON_AREA_INFO Common;
1598 ULONG DeliveryStatus;
1603 if (ProcessorNumber == 0) {
1604 /* Boot processor is already initialized */
1609 if (NextCPU < CPUCount) {
1612 DPRINT("Attempting to boot CPU %d\n", CPU);
1615 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_ASSERT);
1620 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_DEASSERT);
1622 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) {
1623 /* Clear APIC errors */
1624 APICWrite(APIC_ESR, 0);
1625 tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1628 Common = (PCOMMON_AREA_INFO)CommonBase;
1630 /* Write the location of the AP stack */
1631 Common->Stack = (ULONG)ProcessorStack;
1633 DPRINT("CPU %d got stack at 0x%X\n", CPU, Common->Stack);
1635 for (j = 0; j < 16; j++) {
1636 Common->Debug[j] = 0;
1640 maxlvt = APICGetMaxLVT();
1642 /* Is this a local APIC or an 82489DX? */
1643 StartupCount = (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) ? 2 : 0;
1645 for (i = 1; i <= StartupCount; i++)
1647 /* It's a local APIC, so send STARTUP IPI */
1648 DPRINT("Sending startup signal %d\n", i);
1650 APICWrite(APIC_ESR, 0);
1653 APICSendIPI(CPUMap[CPU].APICId,
1655 APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12),
1656 APIC_ICR0_LEVEL_DEASSERT);
1658 /* Wait up to 10ms for IPI to be delivered */
1663 /* Check Delivery Status */
1664 DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1667 } while ((DeliveryStatus) && (j < 1000));
1672 * Due to the Pentium erratum 3AP.
1675 APICRead(APIC_SIVR);
1676 APICWrite(APIC_ESR, 0);
1679 AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1681 if (DeliveryStatus || AcceptStatus) {
1686 if (DeliveryStatus) {
1687 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU);
1691 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU);
1694 if (!(DeliveryStatus || AcceptStatus)) {
1696 /* Wait no more than 5 seconds for processor to boot */
1697 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU);
1699 /* Wait no more than 5 seconds */
1700 for (j = 0; j < 50000; j++) {
1702 if (CPUMap[CPU].Flags & CPU_ENABLED)
1709 if (CPUMap[CPU].Flags & CPU_ENABLED) {
1710 DbgPrint("CPU %d is now running\n", CPU);
1712 DbgPrint("Initialization of CPU %d failed\n", CPU);
1716 DPRINT("Debug bytes are:\n");
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]);
1735 HalAllProcessorsStarted (
1742 return (NextCPU >= CPUCount);
1746 if (BSPInitialized) {
1749 BSPInitialized = TRUE;
1759 HalStartNextProcessor (
1766 /* Display the APIC registers for debugging */
1776 return (NextCPU >= CPUCount);
1790 * Checksum an MP configuration block
1806 static CHAR str[32];
1807 static PCHAR CPUs[] =
1809 "80486DX", "80486DX",
1810 "80486SX", "80486DX/2 or 80487",
1811 "80486SL", "Intel5X2(tm)",
1812 "Unknown", "Unknown",
1816 return ("Pentium(tm) Pro");
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)
1825 sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
1830 static VOID HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
1834 if (!(m->CpuFlags & CPU_FLAG_ENABLED))
1837 DPRINT("Processor #%d %s APIC version %d\n",
1839 HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
1840 (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
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");
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");
1885 if (m->FeatureFlags & (1 << 29))
1886 DPRINT(" Thermal Monitor present.\n");
1887 /* 30, 31 Reserved */
1889 CPUMap[CPUCount].APICId = m->ApicId;
1891 CPUMap[CPUCount].Flags = CPU_USABLE;
1893 if (m->CpuFlags & CPU_FLAG_BSP) {
1894 DPRINT(" Bootup CPU\n");
1895 CPUMap[CPUCount].Flags |= CPU_BSP;
1896 BootCPU = m->ApicId;
1899 if (m->ApicId > MAX_CPU) {
1900 DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
1903 ver = m->ApicVersion;
1909 DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
1912 CPUMap[CPUCount].APICVersion = ver;
1917 static VOID HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
1919 static ULONG CurrentPCIBusId = 0;
1922 memcpy(str, m->BusType, 6);
1924 DPRINT("Bus #%d is %s\n", m->BusId, str);
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;
1934 } else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
1935 BUSMap[m->BusId] = MP_BUS_MCA;
1937 DPRINT("Unknown bustype %s - ignoring\n", str);
1941 static VOID HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
1943 if (!(m->ApicFlags & CPU_FLAG_ENABLED))
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");
1954 IOAPICMap[IOAPICCount].ApicId = m->ApicId;
1955 IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
1956 IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
1960 static VOID HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
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");
1972 IRQMap[IRQCount] = *m;
1976 static VOID HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
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);
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.
1990 if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0)) {
1991 DPRINT1("Invalid MP table!\n");
1994 if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1)) {
1995 DPRINT1("Invalid MP table!\n");
2002 HaliReadMPConfigTable(
2003 PMP_CONFIGURATION_TABLE Table)
2006 Table = Pointer to MP configuration table
2012 if (Table->Signature != MPC_SIGNATURE)
2014 PUCHAR pc = (PUCHAR)&Table->Signature;
2016 DbgPrint("Bad MP configuration block signature: %c%c%c%c\n",
2017 pc[0], pc[1], pc[2], pc[3]);
2022 if (MPChecksum((PUCHAR)Table, Table->Length))
2024 DbgPrint("Bad MP configuration block checksum\n");
2029 if (Table->Specification < 0x04)
2031 DbgPrint("Bad MP configuration table version (%d)\n",
2032 Table->Specification);
2037 APICBase = (PULONG)Table->LocalAPICAddress;
2038 if (APICBase != (PULONG)APIC_DEFAULT_BASE)
2040 DbgPrint("APIC base address is at 0x%X. " \
2041 "I cannot handle non-standard adresses\n", APICBase);
2045 Entry = (PUCHAR)((PVOID)Table + sizeof(MP_CONFIGURATION_TABLE));
2047 while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
2049 /* Switch on type */
2052 case MPCTE_PROCESSOR:
2054 HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
2055 Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
2056 Count += sizeof(MP_CONFIGURATION_PROCESSOR);
2061 HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
2062 Entry += sizeof(MP_CONFIGURATION_BUS);
2063 Count += sizeof(MP_CONFIGURATION_BUS);
2068 HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
2069 Entry += sizeof(MP_CONFIGURATION_IOAPIC);
2070 Count += sizeof(MP_CONFIGURATION_IOAPIC);
2075 HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
2076 Entry += sizeof(MP_CONFIGURATION_INTSRC);
2077 Count += sizeof(MP_CONFIGURATION_INTSRC);
2082 HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
2083 Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
2084 Count += sizeof(MP_CONFIGURATION_INTLOCAL);
2088 DbgPrint("Unknown entry in MPC table\n");
2095 static VOID HaliConstructDefaultIOIrqMPTable(
2098 MP_CONFIGURATION_INTSRC intsrc;
2101 intsrc.Type = MPCTE_INTSRC;
2102 intsrc.IrqFlag = 0; /* conforming */
2103 intsrc.SrcBusId = 0;
2104 intsrc.DstApicId = IOAPICMap[0].ApicId;
2106 intsrc.IrqType = INT_VECTORED;
2107 for (i = 0; i < 16; i++) {
2110 if (i == 0 || i == 13)
2111 continue; /* IRQ0 & IRQ13 not connected */
2115 continue; /* IRQ2 is never connected */
2118 intsrc.SrcBusIrq = i;
2119 intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
2120 HaliMPIntSrcInfo(&intsrc);
2123 intsrc.IrqType = INT_EXTINT;
2124 intsrc.SrcBusIrq = 0;
2125 intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
2126 HaliMPIntSrcInfo(&intsrc);
2130 static VOID HaliConstructDefaultISAMPTable(
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 };
2140 APICBase = (PULONG)APIC_DEFAULT_BASE;
2143 * 2 CPUs, numbered 0 & 1.
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;
2160 bus.Type = MPCTE_BUS;
2164 DPRINT("Unknown standard configuration %d\n", Type);
2168 memcpy(bus.BusType, "ISA ", 6);
2173 memcpy(bus.BusType, "EISA ", 6);
2177 memcpy(bus.BusType, "MCA ", 6);
2179 HaliMPBusInfo(&bus);
2181 bus.Type = MPCTE_BUS;
2183 memcpy(bus.BusType, "PCI ", 6);
2184 HaliMPBusInfo(&bus);
2187 ioapic.Type = MPCTE_IOAPIC;
2189 ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
2190 ioapic.ApicFlags = MP_IOAPIC_USABLE;
2191 ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
2192 HaliMPIOApicInfo(&ioapic);
2195 * We set up most of the low 16 IO-APIC pins according to MPS rules.
2197 HaliConstructDefaultIOIrqMPTable(Type);
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);
2213 HaliScanForMPConfigTable(
2218 Base = Base address of region
2219 Size = Length of region to check
2221 TRUE if a valid MP configuration table was found
2224 PULONG bp = (PULONG)Base;
2225 MP_FLOATING_POINTER* mpf;
2229 if (*bp == MPF_SIGNATURE)
2231 DbgPrint("Found MPF signature at %x, checksum %x\n", bp,
2232 MPChecksum((PUCHAR)bp, 16));
2233 if (MPChecksum((PUCHAR)bp, 16) == 0)
2235 mpf = (MP_FLOATING_POINTER*)bp;
2237 DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
2238 mpf->Specification);
2240 if (mpf->Feature2 & FEATURE2_IMCRP) {
2242 DPRINT("Running in IMCR and PIC compatibility mode.\n")
2245 DPRINT("Running in Virtual Wire compatibility mode.\n");
2248 switch (mpf->Feature1)
2251 /* Non standard configuration */
2257 DPRINT("EISA with no IRQ8 chaining\n");
2266 DPRINT("ISA and PCI\n");
2269 DPRINT("EISA and PCI\n");
2272 DPRINT("MCA and PCI\n");
2275 DbgPrint("Unknown standard configuration %d\n", mpf->Feature1);
2283 if ((mpf->Feature1 == 0) && (mpf->Address)) {
2284 HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)mpf->Address);
2286 HaliConstructDefaultISAMPTable(mpf->Feature1);
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) {
2312 DPRINT("CPU %d says it is now booted.\n", CPU);
2315 APICCalibrateTimer(CPU);
2317 /* This processor is now booted */
2318 CPUMap[CPU].Flags |= CPU_ENABLED;
2319 OnlineCPUs |= (1 << CPU);
2324 MPSInitialized = TRUE;
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
2334 if (!HaliScanForMPConfigTable(0x0, 0x400)) {
2335 if (!HaliScanForMPConfigTable(0x9FC00, 0x400)) {
2336 if (!HaliScanForMPConfigTable(0xF0000, 0x10000)) {
2337 EBDA = *((PUSHORT)0x040E);
2339 if (!HaliScanForMPConfigTable((ULONG)EBDA, 0x1000)) {
2340 DbgPrint("No multiprocessor compliant system found.\n");
2347 /* Setup IRQ to vector translation map */
2348 memset(&IRQVectorMap, sizeof(IRQVectorMap), 0);
2350 /* Initialize the bootstrap processor */
2353 /* Setup I/O APIC */
2356 /* Setup busy waiting */
2357 HalpCalibrateStallExecution();
2359 /* We can now enable interrupts */
2360 __asm__ __volatile__ ("sti\n\t");