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 static BOOLEAN MPSInitialized = FALSE; /* Is the MP system initialized? */
86 static KDPC RescheduleDpc;
88 VOID APICDisable(VOID);
89 static VOID APICSyncArbIDs(VOID);
100 static 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;
152 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN));
162 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
164 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value;
172 IOAPIC_ROUTE_ENTRY Entry;
175 * Disable it in the IO-APIC irq-routing table
177 memset(&Entry, 0, sizeof(Entry));
180 IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0));
181 IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1));
184 static VOID IOAPICClear(
189 for (Pin = 0; Pin < IOAPICMap[Apic].EntryCount; Pin++)
190 IOAPICClearPin(Apic, Pin);
193 static VOID IOAPICClearAll(
198 for (Apic = 0; Apic < IOAPICCount; Apic++)
202 /* This is performance critical and should probably be done in assembler */
207 IOAPIC_ROUTE_ENTRY Entry;
209 *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
212 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
216 /* This is performance critical and should probably be done in assembler */
217 VOID IOAPICUnmaskIrq(
221 IOAPIC_ROUTE_ENTRY Entry;
223 *((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];
593 if (current_vector > FIRST_SYSTEM_VECTOR) {
595 current_vector = FIRST_DEVICE_VECTOR + vector_offset;
596 } else if (current_vector == FIRST_SYSTEM_VECTOR) {
597 DPRINT1("Ran out of interrupt sources!");
601 vector = current_vector;
602 IRQVectorMap[irq] = vector;
608 VOID IOAPICSetupIrqs(
611 IOAPIC_ROUTE_ENTRY entry;
612 ULONG apic, pin, idx, irq, first_notcon = 1, vector;
614 DPRINT("Init IO_APIC IRQs\n");
616 for (apic = 0; apic < IOAPICCount; apic++) {
617 for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++) {
620 * add it to the IO-APIC irq-routing table
622 memset(&entry,0,sizeof(entry));
624 entry.delivery_mode = APIC_DM_LOWEST;
625 entry.dest_mode = 1; /* logical delivery */
626 entry.mask = 0; /* enable IRQ */
627 entry.dest.logical.logical_dest = OnlineCPUs;
629 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED);
632 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap[apic].ApicId, pin);
635 DPRINT(", %d-%d\n", IOAPICMap[apic].ApicId, pin);
640 entry.trigger = IRQTrigger(idx);
641 entry.polarity = IRQPolarity(idx);
646 entry.dest.logical.logical_dest = OnlineCPUs;
649 irq = Pin2Irq(idx, apic, pin);
650 AddPinToIrq(irq, apic, pin);
652 vector = AssignIrqVector(irq);
653 entry.vector = vector;
655 DPRINT("vector 0x%.08x assigned to irq 0x%.02x\n", vector, irq);
663 if ((apic == 0) && (irq < 16))
664 Disable8259AIrq(irq);
666 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1));
667 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0));
673 static VOID IOAPICEnable(
678 for (i = 0; i < PIN_MAP_SIZE; i++) {
679 irq_2_pin[i].pin = -1;
680 irq_2_pin[i].next = 0;
684 * The number of IO-APIC IRQ registers (== #pins):
686 for (i = 0; i < IOAPICCount; i++) {
687 tmp = IOAPICRead(i, IOAPIC_VER);
688 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1;
692 * Do not trust the IO-APIC being empty at bootup
698 static VOID IOAPICDisable(
702 * Clear the IO-APIC before rebooting
711 static VOID IOAPICSetup(
717 // FIXME: This causes application processors to not boot if asked to
724 VOID IOAPICDump(VOID)
727 ULONG reg0, reg1, reg2;
729 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount);
730 for (i = 0; i < IOAPICCount; i++) {
731 DbgPrint("Number of IO-APIC #%d registers: %d.\n",
733 IOAPICMap[i].EntryCount);
737 * We are a bit conservative about what we expect. We have to
738 * know about every hardware change ASAP.
740 DbgPrint("Testing the IO APIC.......................\n");
742 for (apic = 0; apic < IOAPICCount; apic++) {
744 reg0 = IOAPICRead(apic, IOAPIC_ID);
745 reg1 = IOAPICRead(apic, IOAPIC_VER);
746 if (GET_IOAPIC_VERSION(reg1) >= 0x10) {
747 reg2 = IOAPICRead(apic, IOAPIC_ARB);
751 DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId);
752 DbgPrint(".... register #00: %08X\n", reg0);
753 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0));
754 if (reg0 & 0xF0FFFFFF) {
755 DbgPrint(" WARNING: Unexpected IO-APIC\n");
758 DbgPrint(".... register #01: %08X\n", reg1);
759 i = GET_IOAPIC_MRE(reg1);
761 DbgPrint("....... : max redirection entries: %04X\n", i);
762 if ((i != 0x0f) && /* older (Neptune) boards */
763 (i != 0x17) && /* typical ISA+PCI boards */
764 (i != 0x1b) && /* Compaq Proliant boards */
765 (i != 0x1f) && /* dual Xeon boards */
766 (i != 0x22) && /* bigger Xeon boards */
769 DbgPrint(" WARNING: Unexpected IO-APIC\n");
772 i = GET_IOAPIC_VERSION(reg1);
773 DbgPrint("....... : IO APIC version: %04X\n", i);
774 if ((i != 0x01) && /* 82489DX IO-APICs */
775 (i != 0x10) && /* oldest IO-APICs */
776 (i != 0x11) && /* Pentium/Pro IO-APICs */
777 (i != 0x13)) { /* Xeon IO-APICs */
778 DbgPrint(" WARNING: Unexpected IO-APIC\n");
781 if (reg1 & 0xFF00FF00) {
782 DbgPrint(" WARNING: Unexpected IO-APIC\n");
785 if (GET_IOAPIC_VERSION(reg1) >= 0x10) {
786 DbgPrint(".... register #02: %08X\n", reg2);
787 DbgPrint("....... : arbitration: %02X\n",
788 GET_IOAPIC_ARB(reg2));
789 if (reg2 & 0xF0FFFFFF) {
790 DbgPrint(" WARNING: Unexpected IO-APIC\n");
794 DbgPrint(".... IRQ redirection table:\n");
795 DbgPrint(" NR Log Phy Mask Trig IRR Pol"
796 " Stat Dest Deli Vect: \n");
798 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++) {
799 IOAPIC_ROUTE_ENTRY entry;
801 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2);
802 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2);
804 DbgPrint(" %02x %03X %02X ",
806 entry.dest.logical.logical_dest,
807 entry.dest.physical.physical_dest
810 DbgPrint("%C %C %1d %C %C %C %03X %02X\n",
811 (entry.mask == 0) ? 'U' : 'M', // Unmasked/masked
812 (entry.trigger == 0) ? 'E' : 'L', // Edge/level sensitive
814 (entry.polarity == 0) ? 'H' : 'L', // Active high/active low
815 (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending
816 (entry.dest_mode == 0) ? 'P' : 'L', // Physical logical
822 DbgPrint("IRQ to pin mappings:\n");
823 for (i = 0; i < PIC_IRQS; i++) {
824 struct irq_pin_list *entry = irq_2_pin + i;
827 DbgPrint("IRQ%d ", i);
829 DbgPrint("-> %d", entry->pin);
832 entry = irq_2_pin + entry->next;
841 DbgPrint(".................................... done.\n");
846 /* Functions for handling local APICs */
849 volatile inline ULONG APICRead(
854 p = (PULONG)((ULONG)APICBase + Offset);
858 volatile inline ULONG APICRead(
866 //DPRINT1("R(0x%X)", Offset);
867 p = (PULONG)((ULONG)APICBase + Offset);
869 //DPRINT1("(0x%08X)\n", *p);
876 inline VOID APICWrite(
882 p = (PULONG)((ULONG)APICBase + Offset);
887 inline VOID APICWrite(
896 //DPRINT1("W(0x%X, 0x%08X)\n", Offset, Value);
897 p = (PULONG)((ULONG)APICBase + Offset);
904 inline VOID APICSendEOI(VOID)
909 APICWrite(APIC_EOI, 0);
917 if (CPUMap[ThisCPU()].MaxLVT > 3)
918 APICWrite(APIC_ESR, 0);
919 tmp = APICRead(APIC_ESR);
920 DbgPrint("ESR %08x\n", tmp);
924 ULONG APICGetMaxLVT(VOID)
926 ULONG tmp, ver, maxlvt;
928 tmp = APICRead(APIC_VER);
929 ver = GET_APIC_VERSION(tmp);
930 /* 82489DXs do not report # of LVT entries. */
931 maxlvt = APIC_INTEGRATED(ver) ? GET_APIC_MAXLVT(tmp) : 2;
937 static VOID APICClear(
942 maxlvt = CPUMap[ThisCPU()].MaxLVT;
945 * Careful: we have to set masks only first to deassert
946 * any level-triggered sources.
948 tmp = APICRead(APIC_LVTT);
949 APICWrite(APIC_LVTT, tmp | APIC_LVT_MASKED);
951 tmp = APICRead(APIC_LINT0);
952 APICWrite(APIC_LINT0, tmp | APIC_LVT_MASKED);
954 tmp = APICRead(APIC_LINT1);
955 APICWrite(APIC_LINT1, tmp | APIC_LVT_MASKED);
958 tmp = APICRead(APIC_LVT3);
959 APICWrite(APIC_LVT3, tmp | APIC_LVT3_MASKED);
963 tmp = APICRead(APIC_LVTPC);
964 APICWrite(APIC_LVTPC, tmp | APIC_LVT_MASKED);
968 * Clean APIC state for other OSs:
970 APICRead(APIC_SIVR); // Dummy read
971 APICWrite(APIC_LVTT, APIC_LVT_MASKED);
973 APICRead(APIC_SIVR); // Dummy read
974 APICWrite(APIC_LINT0, APIC_LVT_MASKED);
976 APICRead(APIC_SIVR); // Dummy read
977 APICWrite(APIC_LINT1, APIC_LVT_MASKED);
980 APICRead(APIC_SIVR); // Dummy read
981 APICWrite(APIC_LVT3, APIC_LVT3_MASKED);
985 APICRead(APIC_SIVR); // Dummy read
986 APICWrite(APIC_LVTPC, APIC_LVT_MASKED);
990 /* Enable symetric I/O mode ie. connect the BSP's
991 local APIC to INT and NMI lines */
992 inline VOID EnableSMPMode(
996 * Do not trust the local APIC being empty at bootup.
1000 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
1001 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x01);
1005 /* Disable symetric I/O mode ie. go to PIC mode */
1006 inline VOID DisableSMPMode(
1010 * Put the board back into PIC mode (has an effect
1011 * only on certain older boards). Note that APIC
1012 * interrupts, including IPIs, won't work beyond
1013 * this point! The only exception are INIT IPIs.
1015 WRITE_PORT_UCHAR((PUCHAR)0x22, 0x70);
1016 WRITE_PORT_UCHAR((PUCHAR)0x23, 0x00);
1028 * Disable APIC (implies clearing of registers for 82489DX!).
1030 tmp = APICRead(APIC_SIVR);
1031 tmp &= ~APIC_SIVR_ENABLE;
1032 APICWrite(APIC_SIVR, tmp);
1036 inline ULONG ThisCPU(
1039 return (APICRead(APIC_ID) & APIC_ID_MASK) >> 24;
1043 static VOID APICDumpBit(ULONG base)
1047 DbgPrint("0123456789abcdef0123456789abcdef\n");
1048 for (i = 0; i < 8; i++) {
1049 APICRead(base + i*0x10);
1050 for (j = 0; j < 32; j++) {
1063 * Dump the contents of the local APIC registers
1066 ULONG v, ver, maxlvt;
1067 ULONG r1, r2, w1, w2;
1074 DbgPrint("\nPrinting local APIC contents on CPU(%d):\n", ThisCPU());
1075 v = APICRead(APIC_ID);
1076 DbgPrint("... ID : %08x (%01x) ", v, GET_APIC_ID(v));
1077 v = APICRead(APIC_VER);
1078 DbgPrint("... VERSION: %08x\n", v);
1079 ver = GET_APIC_VERSION(v);
1080 maxlvt = CPUMap[ThisCPU()].MaxLVT;
1082 v = APICRead(APIC_TPR);
1083 DbgPrint("... TPR : %08x (%02x)", v, v & ~0);
1085 if (APIC_INTEGRATED(ver)) { /* !82489DX */
1086 v = APICRead(APIC_APR);
1087 DbgPrint("... APR : %08x (%02x)\n", v, v & ~0);
1088 v = APICRead(APIC_PPR);
1089 DbgPrint("... PPR : %08x\n", v);
1092 v = APICRead(APIC_EOI);
1093 DbgPrint("... EOI : %08x ! ", v);
1094 v = APICRead(APIC_LDR);
1095 DbgPrint("... LDR : %08x\n", v);
1096 v = APICRead(APIC_DFR);
1097 DbgPrint("... DFR : %08x ! ", v);
1098 v = APICRead(APIC_SIVR);
1099 DbgPrint("... SIVR : %08x\n", v);
1103 DbgPrint("... ISR field:\n");
1104 APICDumpBit(APIC_ISR);
1105 DbgPrint("... TMR field:\n");
1106 APICDumpBit(APIC_TMR);
1107 DbgPrint("... IRR field:\n");
1108 APICDumpBit(APIC_IRR);
1111 if (APIC_INTEGRATED(ver)) { /* !82489DX */
1112 if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
1113 APICWrite(APIC_ESR, 0);
1114 v = APICRead(APIC_ESR);
1115 DbgPrint("... ESR : %08x\n", v);
1118 v = APICRead(APIC_ICR0);
1119 DbgPrint("... ICR0 : %08x ! ", v);
1120 v = APICRead(APIC_ICR1);
1121 DbgPrint("... ICR1 : %08x ! ", v);
1123 v = APICRead(APIC_LVTT);
1124 DbgPrint("... LVTT : %08x\n", v);
1126 if (maxlvt > 3) { /* PC is LVT#4. */
1127 v = APICRead(APIC_LVTPC);
1128 DbgPrint("... LVTPC : %08x ! ", v);
1130 v = APICRead(APIC_LINT0);
1131 DbgPrint("... LINT0 : %08x ! ", v);
1132 v = APICRead(APIC_LINT1);
1133 DbgPrint("... LINT1 : %08x\n", v);
1136 v = APICRead(APIC_LVT3);
1137 DbgPrint("... LVT3 : %08x\n", v);
1140 v = APICRead(APIC_ICRT);
1141 DbgPrint("... ICRT : %08x ! ", v);
1142 v = APICRead(APIC_CCRT);
1143 DbgPrint("... CCCT : %08x ! ", v);
1144 v = APICRead(APIC_TDCR);
1145 DbgPrint("... TDCR : %08x\n", v);
1147 DbgPrint("Last register read (offset): 0x%08X\n", r1);
1148 DbgPrint("Last register read (value): 0x%08X\n", r2);
1149 DbgPrint("Last register written (offset): 0x%08X\n", w1);
1150 DbgPrint("Last register written (value): 0x%08X\n", w2);
1155 ULONG Read8254Timer(VOID)
1159 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
1160 Count = READ_PORT_UCHAR((PUCHAR)0x40);
1161 Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
1167 VOID WaitFor8254Wraparound(VOID)
1169 ULONG CurCount, PrevCount = ~0;
1172 CurCount = Read8254Timer();
1174 PrevCount = CurCount;
1175 CurCount = Read8254Timer();
1176 Delta = CurCount - PrevCount;
1179 * This limit for delta seems arbitrary, but it isn't, it's
1180 * slightly above the level of error a buggy Mercury/Neptune
1181 * chipset timer can cause.
1184 } while (Delta < 300);
1188 #define APIC_DIVISOR (16)
1195 /* Periodic timer */
1196 tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
1197 APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;
1198 APICWrite(APIC_LVTT, tmp);
1200 tmp = APICRead(APIC_TDCR);
1201 tmp &= ~(APIC_TDCR_1 | APIC_TDCR_TMBASE | APIC_TDCR_16);
1202 APICWrite(APIC_TDCR, tmp);
1203 APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);
1207 VOID APICCalibrateTimer(
1210 ULARGE_INTEGER t1, t2;
1213 DPRINT("Calibrating APIC timer...\n");
1218 * The timer chip counts down to zero. Let's wait
1219 * for a wraparound to start exact measurement:
1220 * (the current tick might have been already half done)
1222 WaitFor8254Wraparound();
1225 * We wrapped around just now. Let's start
1227 ReadPentiumClock(&t1);
1228 tt1 = APICRead(APIC_CCRT);
1230 WaitFor8254Wraparound();
1232 tt2 = APICRead(APIC_CCRT);
1233 ReadPentiumClock(&t2);
1235 CPUMap[CPU].BusSpeed = (HZ * (tt2 - tt1) * APIC_DIVISOR);
1236 CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
1238 /* Setup timer for normal operation */
1239 //APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100); // 100ns
1240 // APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 15000); // 15ms
1241 APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
1243 DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
1244 CPUMap[CPU].CoreSpeed/1000000,
1245 CPUMap[CPU].CoreSpeed%1000000);
1247 DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
1248 CPUMap[CPU].BusSpeed/1000000,
1249 CPUMap[CPU].BusSpeed%1000000);
1253 static VOID APICSleep(
1257 Count = Number of microseconds to busy wait
1260 KeStallExecutionProcessor(Count);
1264 static VOID APICSyncArbIDs(
1269 /* Wait up to 100ms for the APIC to become ready */
1270 for (i = 0; i < 10000; i++) {
1271 tmp = APICRead(APIC_ICR0);
1272 /* Check Delivery Status */
1273 if ((tmp & APIC_ICR0_DS) == 0)
1279 DPRINT("CPU(%d) APIC busy for 100ms.\n", ThisCPU());
1282 DPRINT("Synchronizing Arb IDs.\n");
1283 APICWrite(APIC_ICR0, APIC_ICR0_DESTS_ALL | APIC_ICR0_LEVEL | APIC_DM_INIT);
1293 ULONG tmp, i, flags;
1296 __asm__ ("\n\tcli\n\t");
1298 /* Wait up to 100ms for the APIC to become ready */
1299 for (i = 0; i < 10000; i++) {
1300 tmp = APICRead(APIC_ICR0);
1301 /* Check Delivery Status */
1302 if ((tmp & APIC_ICR0_DS) == 0)
1308 DPRINT("CPU(%d) Previous IPI was not delivered after 100ms.\n", ThisCPU());
1311 /* Setup the APIC to deliver the IPI */
1312 tmp = APICRead(APIC_ICR1);
1314 APICWrite(APIC_ICR1, tmp | SET_APIC_DEST_FIELD(Target));
1316 tmp = APICRead(APIC_ICR0);
1317 tmp &= ~(APIC_ICR0_LEVEL | APIC_ICR0_DESTM | APIC_ICR0_DM | APIC_ICR0_VECTOR);
1318 tmp |= (DeliveryMode | IntNum | Level);
1320 if (Target == APIC_TARGET_SELF) {
1321 tmp |= APIC_ICR0_DESTS_SELF;
1322 } else if (Target == APIC_TARGET_ALL) {
1323 tmp |= APIC_ICR0_DESTS_ALL;
1324 } else if (Target == APIC_TARGET_ALL_BUT_SELF) {
1325 tmp |= APIC_ICR0_DESTS_ALL_BUT_SELF;
1327 tmp |= APIC_ICR0_DESTS_FIELD;
1330 /* Now, fire off the IPI */
1331 APICWrite(APIC_ICR0, tmp);
1337 BOOLEAN VerifyLocalAPIC(
1342 /* The version register is read-only in a real APIC */
1343 reg0 = APICRead(APIC_VER);
1344 APICWrite(APIC_VER, reg0 ^ APIC_VER_MASK);
1345 reg1 = APICRead(APIC_VER);
1350 /* The ID register is read/write in a real APIC */
1351 reg0 = APICRead(APIC_ID);
1352 APICWrite(APIC_ID, reg0 ^ APIC_ID_MASK);
1353 reg1 = APICRead(APIC_ID);
1354 APICWrite(APIC_ID, reg0);
1355 if (reg1 != (reg0 ^ APIC_ID_MASK))
1362 static VOID SetInterruptGate(
1366 IDT_DESCRIPTOR *idt;
1368 idt = (IDT_DESCRIPTOR*)((ULONG)KeGetCurrentKPCR()->IDT + index * sizeof(IDT_DESCRIPTOR));
1369 idt->a = (((ULONG)address)&0xffff) + (KERNEL_CS << 16);
1370 idt->b = 0x8f00 + (((ULONG)address)&0xffff0000);
1374 VOID MpsTimerHandler(
1383 * Notify the rest of the kernel of the raised irq level
1385 //OldIrql = KeRaiseIrqlToSynchLevel();
1386 KeRaiseIrql(PROFILE_LEVEL, &OldIrql);
1390 * NOTE: Only higher priority interrupts will get through
1394 if (KeGetCurrentProcessorNumber() == 0)
1397 //KeLowerIrql(PROFILE_LEVEL);
1398 KiInterruptDispatch2(OldIrql, 0);
1399 //KeRaiseIrql(CLOCK2_LEVEL, &OldIrql2);
1402 DbgPrint("MpsTimerHandler() called at IRQL 0x%.08x\n", OldIrql);
1403 //(BOOLEAN) KeInsertQueueDpc(&RescheduleDpc, NULL, NULL);
1405 DbgPrint("MpsTimerHandler() -1 IRQL 0x%.08x\n", OldIrql);
1408 * Disable interrupts
1412 DbgPrint("MpsTimerHandler() 0 IRQL 0x%.08x\n", OldIrql);
1415 * Acknowledge the interrupt
1422 DbgPrint("MpsTimerHandler() 1 IRQL 0x%.08x\n", OldIrql);
1423 KeLowerIrql(OldIrql);
1424 DbgPrint("MpsTimerHandler() 2 IRQL 0x%.08x\n", OldIrql);
1429 VOID MpsErrorHandler(
1436 tmp1 = APICRead(APIC_ESR);
1437 APICWrite(APIC_ESR, 0);
1438 tmp2 = APICRead(APIC_ESR);
1441 * Acknowledge the interrupt
1445 /* Here is what the APIC error bits mean:
1448 2: Send accept error
1449 3: Receive accept error
1451 5: Send illegal vector
1452 6: Received illegal vector
1453 7: Illegal register address
1455 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
1460 VOID MpsSpuriousHandler(
1463 DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
1465 /* No need to send EOI here */
1480 * Intel recommends to set DFR, LDR and TPR before enabling
1481 * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
1482 * document number 292116). So here it goes...
1486 * Put the APIC into flat delivery mode.
1487 * Must be "all ones" explicitly for 82489DX.
1489 APICWrite(APIC_DFR, 0xFFFFFFFF);
1492 * Set up the logical destination ID.
1494 tmp = APICRead(APIC_LDR);
1495 tmp &= ~APIC_LDR_MASK;
1496 tmp |= (1 << (CPU + 24));
1497 APICWrite(APIC_LDR, tmp);
1499 /* Accept all interrupts */
1500 tmp = (APICRead(APIC_TPR) & ~APIC_TPR_PRI);
1501 APICWrite(APIC_TPR, tmp);
1503 /* Enable local APIC */
1504 tmp = APICRead(APIC_SIVR) | APIC_SIVR_ENABLE | APIC_SIVR_FOCUS; // No focus processor
1506 /* Set spurious interrupt vector */
1507 tmp |= SPURIOUS_VECTOR;
1509 APICWrite(APIC_SIVR, tmp);
1512 * Only the BSP should see the LINT1 NMI signal, obviously.
1517 tmp = APIC_DM_NMI | APIC_LVT_MASKED;
1518 if (!APIC_INTEGRATED(CPUMap[CPU].APICVersion)) /* 82489DX */
1519 tmp |= APIC_LVT_LEVEL_TRIGGER;
1520 APICWrite(APIC_LINT1, tmp);
1522 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) { /* !82489DX */
1523 if (CPUMap[CPU].MaxLVT > 3) { /* Due to the Pentium erratum 3AP */
1524 APICWrite(APIC_ESR, 0);
1527 tmp = APICRead(APIC_ESR);
1528 DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
1530 /* Enable sending errors */
1532 APICWrite(APIC_LVT3, tmp);
1535 * Spec says clear errors after enabling vector
1537 if (CPUMap[CPU].MaxLVT > 3)
1538 APICWrite(APIC_ESR, 0);
1539 tmp = APICRead(APIC_ESR);
1540 DPRINT("ESR value after enabling vector: 0x%X\n", tmp);
1550 /* Only initialize the BSP once */
1554 BSPInitialized = TRUE;
1556 DPRINT("APIC is mapped at 0x%X\n", APICBase);
1558 if (VerifyLocalAPIC()) {
1559 DPRINT("APIC found\n");
1561 DPRINT1("No APIC found\n");
1565 CPUMap[BootCPU].MaxLVT = APICGetMaxLVT();
1567 SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
1568 SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
1569 SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
1571 if (APICMode == amPIC) {
1577 /* BIOS data segment */
1578 BIOSBase = (PULONG)BIOS_AREA;
1580 /* Area for communicating with the APs */
1581 CommonBase = (PULONG)COMMON_AREA;
1583 /* Copy bootstrap code to common area */
1584 memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
1586 (ULONG)&APend - (ULONG)&APstart + 1);
1588 /* Set shutdown code */
1589 CMOS_WRITE(0xF, 0xA);
1591 /* Set warm reset vector */
1592 ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
1593 *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
1595 ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
1596 *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
1598 /* Calibrate APIC timer */
1599 APICCalibrateTimer(0);
1601 /* The boot processor is online */
1602 OnlineCPUs = (1 << 0);
1609 HalInitializeProcessor (
1610 ULONG ProcessorNumber,
1611 PVOID ProcessorStack)
1616 PCOMMON_AREA_INFO Common;
1618 ULONG DeliveryStatus;
1623 if (ProcessorNumber == 0) {
1624 /* Boot processor is already initialized */
1629 if (NextCPU < CPUCount) {
1632 DPRINT("Attempting to boot CPU %d\n", CPU);
1635 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_ASSERT);
1640 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_DEASSERT);
1642 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) {
1643 /* Clear APIC errors */
1644 APICWrite(APIC_ESR, 0);
1645 tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1648 Common = (PCOMMON_AREA_INFO)CommonBase;
1650 /* Write the location of the AP stack */
1651 Common->Stack = (ULONG)ProcessorStack;
1653 DPRINT("CPU %d got stack at 0x%X\n", CPU, Common->Stack);
1655 for (j = 0; j < 16; j++) {
1656 Common->Debug[j] = 0;
1660 maxlvt = APICGetMaxLVT();
1662 /* Is this a local APIC or an 82489DX? */
1663 StartupCount = (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) ? 2 : 0;
1665 for (i = 1; i <= StartupCount; i++)
1667 /* It's a local APIC, so send STARTUP IPI */
1668 DPRINT("Sending startup signal %d\n", i);
1670 APICWrite(APIC_ESR, 0);
1673 APICSendIPI(CPUMap[CPU].APICId,
1675 APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12),
1676 APIC_ICR0_LEVEL_DEASSERT);
1678 /* Wait up to 10ms for IPI to be delivered */
1683 /* Check Delivery Status */
1684 DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1687 } while ((DeliveryStatus) && (j < 1000));
1692 * Due to the Pentium erratum 3AP.
1695 APICRead(APIC_SIVR);
1696 APICWrite(APIC_ESR, 0);
1699 AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1701 if (DeliveryStatus || AcceptStatus) {
1706 if (DeliveryStatus) {
1707 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU);
1711 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU);
1714 if (!(DeliveryStatus || AcceptStatus)) {
1716 /* Wait no more than 5 seconds for processor to boot */
1717 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU);
1719 /* Wait no more than 5 seconds */
1720 for (j = 0; j < 50000; j++) {
1722 if (CPUMap[CPU].Flags & CPU_ENABLED)
1729 if (CPUMap[CPU].Flags & CPU_ENABLED) {
1730 DbgPrint("CPU %d is now running\n", CPU);
1732 DbgPrint("Initialization of CPU %d failed\n", CPU);
1736 DPRINT("Debug bytes are:\n");
1738 for (j = 0; j < 4; j++) {
1739 DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1740 Common->Debug[j*4+0],
1741 Common->Debug[j*4+1],
1742 Common->Debug[j*4+2],
1743 Common->Debug[j*4+3]);
1755 HalAllProcessorsStarted (
1762 //return (NextCPU >= CPUCount);
1763 return (NextCPU >= 1);
1767 if (BSPInitialized) {
1770 BSPInitialized = TRUE;
1780 HalStartNextProcessor (
1787 /* Display the APIC registers for debugging */
1797 return (NextCPU >= CPUCount);
1811 * Checksum an MP configuration block
1827 static CHAR str[32];
1828 static PCHAR CPUs[] =
1830 "80486DX", "80486DX",
1831 "80486SX", "80486DX/2 or 80487",
1832 "80486SL", "Intel5X2(tm)",
1833 "Unknown", "Unknown",
1837 return ("Pentium(tm) Pro");
1839 return ("Pentium(tm)");
1840 if (Family == 0x0F && Model == 0x0F)
1841 return("Special controller");
1842 if (Family == 0x0F && Model == 0x00)
1843 return("Pentium 4(tm)");
1844 if (Family == 0x04 && Model < 9)
1846 sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
1851 static VOID HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
1855 if (!(m->CpuFlags & CPU_FLAG_ENABLED))
1858 DPRINT("Processor #%d %s APIC version %d\n",
1860 HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
1861 (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
1864 if (m->FeatureFlags & (1 << 0))
1865 DPRINT(" Floating point unit present.\n");
1866 if (m->FeatureFlags & (1 << 7))
1867 DPRINT(" Machine Exception supported.\n");
1868 if (m->FeatureFlags & (1 << 8))
1869 DPRINT(" 64 bit compare & exchange supported.\n");
1870 if (m->FeatureFlags & (1 << 9))
1871 DPRINT(" Internal APIC present.\n");
1872 if (m->FeatureFlags & (1 << 11))
1873 DPRINT(" SEP present.\n");
1874 if (m->FeatureFlags & (1 << 12))
1875 DPRINT(" MTRR present.\n");
1876 if (m->FeatureFlags & (1 << 13))
1877 DPRINT(" PGE present.\n");
1878 if (m->FeatureFlags & (1 << 14))
1879 DPRINT(" MCA present.\n");
1880 if (m->FeatureFlags & (1 << 15))
1881 DPRINT(" CMOV present.\n");
1882 if (m->FeatureFlags & (1 << 16))
1883 DPRINT(" PAT present.\n");
1884 if (m->FeatureFlags & (1 << 17))
1885 DPRINT(" PSE present.\n");
1886 if (m->FeatureFlags & (1 << 18))
1887 DPRINT(" PSN present.\n");
1888 if (m->FeatureFlags & (1 << 19))
1889 DPRINT(" Cache Line Flush Instruction present.\n");
1891 if (m->FeatureFlags & (1 << 21))
1892 DPRINT(" Debug Trace and EMON Store present.\n");
1893 if (m->FeatureFlags & (1 << 22))
1894 DPRINT(" ACPI Thermal Throttle Registers present.\n");
1895 if (m->FeatureFlags & (1 << 23))
1896 DPRINT(" MMX present.\n");
1897 if (m->FeatureFlags & (1 << 24))
1898 DPRINT(" FXSR present.\n");
1899 if (m->FeatureFlags & (1 << 25))
1900 DPRINT(" XMM present.\n");
1901 if (m->FeatureFlags & (1 << 26))
1902 DPRINT(" Willamette New Instructions present.\n");
1903 if (m->FeatureFlags & (1 << 27))
1904 DPRINT(" Self Snoop present.\n");
1906 if (m->FeatureFlags & (1 << 29))
1907 DPRINT(" Thermal Monitor present.\n");
1908 /* 30, 31 Reserved */
1910 CPUMap[CPUCount].APICId = m->ApicId;
1912 CPUMap[CPUCount].Flags = CPU_USABLE;
1914 if (m->CpuFlags & CPU_FLAG_BSP) {
1915 DPRINT(" Bootup CPU\n");
1916 CPUMap[CPUCount].Flags |= CPU_BSP;
1917 BootCPU = m->ApicId;
1920 if (m->ApicId > MAX_CPU) {
1921 DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
1924 ver = m->ApicVersion;
1930 DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
1933 CPUMap[CPUCount].APICVersion = ver;
1938 static VOID HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
1940 static ULONG CurrentPCIBusId = 0;
1943 memcpy(str, m->BusType, 6);
1945 DPRINT("Bus #%d is %s\n", m->BusId, str);
1947 if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) {
1948 BUSMap[m->BusId] = MP_BUS_ISA;
1949 } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) {
1950 BUSMap[m->BusId] = MP_BUS_EISA;
1951 } else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) {
1952 BUSMap[m->BusId] = MP_BUS_PCI;
1953 PCIBUSMap[m->BusId] = CurrentPCIBusId;
1955 } else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
1956 BUSMap[m->BusId] = MP_BUS_MCA;
1958 DPRINT("Unknown bustype %s - ignoring\n", str);
1962 static VOID HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
1964 if (!(m->ApicFlags & CPU_FLAG_ENABLED))
1967 DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
1968 m->ApicId, m->ApicVersion, m->ApicAddress);
1969 if (IOAPICCount > MAX_IOAPIC) {
1970 DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
1971 MAX_IOAPIC, IOAPICCount);
1972 DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
1975 IOAPICMap[IOAPICCount].ApicId = m->ApicId;
1976 IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
1977 IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
1981 static VOID HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
1983 DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
1984 " IRQ %02x, APIC ID %x, APIC INT %02x\n",
1985 m->IrqType, m->IrqFlag & 3,
1986 (m->IrqFlag >> 2) & 3, m->SrcBusId,
1987 m->SrcBusIrq, m->DstApicId, m->DstApicInt);
1988 if (IRQCount > MAX_IRQ_SOURCE) {
1989 DPRINT1("Max # of irq sources exceeded!!\n");
1993 IRQMap[IRQCount] = *m;
1997 static VOID HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
1999 DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
2000 " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
2001 m->IrqType, m->IrqFlag & 3,
2002 (m->IrqFlag >> 2) & 3, m->SrcBusId,
2003 m->SrcBusIrq, m->DstApicId, m->DstApicLInt);
2005 * Well it seems all SMP boards in existence
2006 * use ExtINT/LVT1 == LINT0 and
2007 * NMI/LVT2 == LINT1 - the following check
2008 * will show us if this assumptions is false.
2009 * Until then we do not have to add baggage.
2011 if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0)) {
2012 DPRINT1("Invalid MP table!\n");
2015 if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1)) {
2016 DPRINT1("Invalid MP table!\n");
2023 HaliReadMPConfigTable(
2024 PMP_CONFIGURATION_TABLE Table)
2027 Table = Pointer to MP configuration table
2033 if (Table->Signature != MPC_SIGNATURE)
2035 PUCHAR pc = (PUCHAR)&Table->Signature;
2037 DbgPrint("Bad MP configuration block signature: %c%c%c%c\n",
2038 pc[0], pc[1], pc[2], pc[3]);
2043 if (MPChecksum((PUCHAR)Table, Table->Length))
2045 DbgPrint("Bad MP configuration block checksum\n");
2050 if (Table->Specification < 0x04)
2052 DbgPrint("Bad MP configuration table version (%d)\n",
2053 Table->Specification);
2058 APICBase = (PULONG)Table->LocalAPICAddress;
2059 if (APICBase != (PULONG)APIC_DEFAULT_BASE)
2061 DbgPrint("APIC base address is at 0x%X. " \
2062 "I cannot handle non-standard adresses\n", APICBase);
2066 Entry = (PUCHAR)((PVOID)Table + sizeof(MP_CONFIGURATION_TABLE));
2068 while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
2070 /* Switch on type */
2073 case MPCTE_PROCESSOR:
2075 HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
2076 Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
2077 Count += sizeof(MP_CONFIGURATION_PROCESSOR);
2082 HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
2083 Entry += sizeof(MP_CONFIGURATION_BUS);
2084 Count += sizeof(MP_CONFIGURATION_BUS);
2089 HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
2090 Entry += sizeof(MP_CONFIGURATION_IOAPIC);
2091 Count += sizeof(MP_CONFIGURATION_IOAPIC);
2096 HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
2097 Entry += sizeof(MP_CONFIGURATION_INTSRC);
2098 Count += sizeof(MP_CONFIGURATION_INTSRC);
2103 HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
2104 Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
2105 Count += sizeof(MP_CONFIGURATION_INTLOCAL);
2109 DbgPrint("Unknown entry in MPC table\n");
2116 static VOID HaliConstructDefaultIOIrqMPTable(
2119 MP_CONFIGURATION_INTSRC intsrc;
2122 intsrc.Type = MPCTE_INTSRC;
2123 intsrc.IrqFlag = 0; /* conforming */
2124 intsrc.SrcBusId = 0;
2125 intsrc.DstApicId = IOAPICMap[0].ApicId;
2127 intsrc.IrqType = INT_VECTORED;
2128 for (i = 0; i < 16; i++) {
2131 if (i == 0 || i == 13)
2132 continue; /* IRQ0 & IRQ13 not connected */
2136 continue; /* IRQ2 is never connected */
2139 intsrc.SrcBusIrq = i;
2140 intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
2141 HaliMPIntSrcInfo(&intsrc);
2144 intsrc.IrqType = INT_EXTINT;
2145 intsrc.SrcBusIrq = 0;
2146 intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
2147 HaliMPIntSrcInfo(&intsrc);
2151 static VOID HaliConstructDefaultISAMPTable(
2154 MP_CONFIGURATION_PROCESSOR processor;
2155 MP_CONFIGURATION_BUS bus;
2156 MP_CONFIGURATION_IOAPIC ioapic;
2157 MP_CONFIGURATION_INTLOCAL lintsrc;
2158 ULONG linttypes[2] = { INT_EXTINT, INT_NMI };
2161 APICBase = (PULONG)APIC_DEFAULT_BASE;
2164 * 2 CPUs, numbered 0 & 1.
2166 processor.Type = MPCTE_PROCESSOR;
2167 /* Either an integrated APIC or a discrete 82489DX. */
2168 processor.ApicVersion = Type > 4 ? 0x10 : 0x01;
2169 processor.CpuFlags = CPU_FLAG_ENABLED | CPU_FLAG_BSP;
2170 /* FIXME: Get this from the bootstrap processor */
2171 processor.CpuSignature = 0;
2172 processor.FeatureFlags = 0;
2173 processor.Reserved[0] = 0;
2174 processor.Reserved[1] = 0;
2175 for (i = 0; i < 2; i++) {
2176 processor.ApicId = i;
2177 HaliMPProcessorInfo(&processor);
2178 processor.CpuFlags &= ~CPU_FLAG_BSP;
2181 bus.Type = MPCTE_BUS;
2185 DPRINT("Unknown standard configuration %d\n", Type);
2189 memcpy(bus.BusType, "ISA ", 6);
2194 memcpy(bus.BusType, "EISA ", 6);
2198 memcpy(bus.BusType, "MCA ", 6);
2200 HaliMPBusInfo(&bus);
2202 bus.Type = MPCTE_BUS;
2204 memcpy(bus.BusType, "PCI ", 6);
2205 HaliMPBusInfo(&bus);
2208 ioapic.Type = MPCTE_IOAPIC;
2210 ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
2211 ioapic.ApicFlags = MP_IOAPIC_USABLE;
2212 ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
2213 HaliMPIOApicInfo(&ioapic);
2216 * We set up most of the low 16 IO-APIC pins according to MPS rules.
2218 HaliConstructDefaultIOIrqMPTable(Type);
2220 lintsrc.Type = MPCTE_LINTSRC;
2221 lintsrc.IrqType = 0;
2222 lintsrc.IrqFlag = 0; /* conforming */
2223 lintsrc.SrcBusId = 0;
2224 lintsrc.SrcBusIrq = 0;
2225 lintsrc.DstApicId = MP_APIC_ALL;
2226 for (i = 0; i < 2; i++) {
2227 lintsrc.IrqType = linttypes[i];
2228 lintsrc.DstApicLInt = i;
2229 HaliMPIntLocalInfo(&lintsrc);
2235 HaliScanForMPConfigTable(
2240 Base = Base address of region
2241 Size = Length of region to check
2243 TRUE if a valid MP configuration table was found
2246 PULONG bp = (PULONG)Base;
2247 MP_FLOATING_POINTER* mpf;
2251 if (*bp == MPF_SIGNATURE)
2253 DbgPrint("Found MPF signature at %x, checksum %x\n", bp,
2254 MPChecksum((PUCHAR)bp, 16));
2255 if (MPChecksum((PUCHAR)bp, 16) == 0)
2257 mpf = (MP_FLOATING_POINTER*)bp;
2259 DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
2260 mpf->Specification);
2262 if (mpf->Feature2 & FEATURE2_IMCRP) {
2264 DPRINT("Running in IMCR and PIC compatibility mode.\n")
2267 DPRINT("Running in Virtual Wire compatibility mode.\n");
2270 switch (mpf->Feature1)
2273 /* Non standard configuration */
2279 DPRINT("EISA with no IRQ8 chaining\n");
2288 DPRINT("ISA and PCI\n");
2291 DPRINT("EISA and PCI\n");
2294 DPRINT("MCA and PCI\n");
2297 DbgPrint("Unknown standard configuration %d\n", mpf->Feature1);
2305 if ((mpf->Feature1 == 0) && (mpf->Address)) {
2306 HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)mpf->Address);
2308 HaliConstructDefaultISAMPTable(mpf->Feature1);
2322 RescheduleDpcRoutine(PKDPC Dpc, PVOID DeferredContext,
2323 PVOID SystemArgument1, PVOID SystemArgument2)
2329 NewIrql = KeGetCurrentIrql();
2330 KeLowerIrql(APC_LEVEL);
2331 KeRescheduleThread();
2332 KeRaiseIrql(NewIrql, &OldIrql);
2344 /* Only initialize MP system once. Once called the first time,
2345 each subsequent call is part of the initialization sequence
2346 for an application processor. */
2347 if (MPSInitialized) {
2350 DPRINT("CPU %d says it is now booted.\n", CPU);
2353 APICCalibrateTimer(CPU);
2355 /* This processor is now booted */
2356 CPUMap[CPU].Flags |= CPU_ENABLED;
2357 OnlineCPUs |= (1 << CPU);
2362 MPSInitialized = TRUE;
2364 KeInitializeDpc(&RescheduleDpc, RescheduleDpcRoutine, NULL);
2367 Scan the system memory for an MP configuration table
2368 1) Scan the first KB of system base memory
2369 2) Scan the last KB of system base memory
2370 3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
2371 4) Scan the Extended BIOS Data Area
2374 if (!HaliScanForMPConfigTable(0x0, 0x400)) {
2375 if (!HaliScanForMPConfigTable(0x9FC00, 0x400)) {
2376 if (!HaliScanForMPConfigTable(0xF0000, 0x10000)) {
2377 EBDA = *((PUSHORT)0x040E);
2379 if (!HaliScanForMPConfigTable((ULONG)EBDA, 0x1000)) {
2380 DbgPrint("No multiprocessor compliant system found.\n");
2387 /* Setup IRQ to vector translation map */
2388 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap));
2390 /* Initialize the bootstrap processor */
2393 /* Setup I/O APIC */
2396 /* Setup busy waiting */
2397 HalpCalibrateStallExecution();
2399 /* We can now enable interrupts */
2400 __asm__ __volatile__ ("sti\n\t");