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 // TODO FIXME - What happened to definition for PsDispatchThread ???
1397 //PsDispatchThread(THREAD_STATE_READY);
1399 // KeGetCurrentThread is linked into hal from ntoskrnl, so can
1400 // PsDispatchThread be exported from ntoskrnl also ???
1407 KeLowerIrql(OldIrql);
1412 VOID MpsErrorHandler(
1419 tmp1 = APICRead(APIC_ESR);
1420 APICWrite(APIC_ESR, 0);
1421 tmp2 = APICRead(APIC_ESR);
1424 * Acknowledge the interrupt
1428 /* Here is what the APIC error bits mean:
1431 2: Send accept error
1432 3: Receive accept error
1434 5: Send illegal vector
1435 6: Received illegal vector
1436 7: Illegal register address
1438 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1, tmp2);
1443 VOID MpsSpuriousHandler(
1446 DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
1449 * Acknowledge the interrupt
1465 * Intel recommends to set DFR, LDR and TPR before enabling
1466 * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
1467 * document number 292116). So here it goes...
1471 * Put the APIC into flat delivery mode.
1472 * Must be "all ones" explicitly for 82489DX.
1474 APICWrite(APIC_DFR, 0xFFFFFFFF);
1477 * Set up the logical destination ID.
1479 tmp = APICRead(APIC_LDR);
1480 tmp &= ~APIC_LDR_MASK;
1481 tmp |= (1 << (CPU + 24));
1482 APICWrite(APIC_LDR, tmp);
1484 /* Accept all interrupts */
1485 tmp = (APICRead(APIC_TPR) & ~APIC_TPR_PRI);
1486 APICWrite(APIC_TPR, tmp);
1488 /* Enable local APIC */
1489 tmp = APICRead(APIC_SIVR) | APIC_SIVR_ENABLE | APIC_SIVR_FOCUS; // No focus processor
1491 /* Set spurious interrupt vector */
1492 tmp |= SPURIOUS_VECTOR;
1494 APICWrite(APIC_SIVR, tmp);
1497 * Only the BP should see the LINT1 NMI signal, obviously.
1502 tmp = APIC_DM_NMI | APIC_LVT_MASKED;
1503 if (!APIC_INTEGRATED(CPUMap[CPU].APICVersion)) /* 82489DX */
1504 tmp |= APIC_LVT_LEVEL_TRIGGER;
1505 APICWrite(APIC_LINT1, tmp);
1507 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) { /* !82489DX */
1508 if (CPUMap[CPU].MaxLVT > 3) { /* Due to the Pentium erratum 3AP */
1509 APICWrite(APIC_ESR, 0);
1512 tmp = APICRead(APIC_ESR);
1513 DPRINT("ESR value before enabling vector: 0x%X\n", tmp);
1515 /* Enable sending errors */
1517 APICWrite(APIC_LVT3, tmp);
1520 * Spec says clear errors after enabling vector
1522 if (CPUMap[CPU].MaxLVT > 3)
1523 APICWrite(APIC_ESR, 0);
1524 tmp = APICRead(APIC_ESR);
1525 DPRINT("ESR value after enabling vector: 0x%X\n", tmp);
1535 /* Only initialize the BSP once */
1539 BSPInitialized = TRUE;
1541 DPRINT("APIC is mapped at 0x%X\n", APICBase);
1543 if (VerifyLocalAPIC()) {
1544 DPRINT("APIC found\n");
1546 DPRINT1("No APIC found\n");
1550 CPUMap[BootCPU].MaxLVT = APICGetMaxLVT();
1552 SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
1553 SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
1554 SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
1556 if (APICMode == amPIC) {
1562 /* BIOS data segment */
1563 BIOSBase = (PULONG)BIOS_AREA;
1565 /* Area for communicating with the APs */
1566 CommonBase = (PULONG)COMMON_AREA;
1568 /* Copy bootstrap code to common area */
1569 memcpy((PVOID)((ULONG)CommonBase + PAGE_SIZE),
1571 (ULONG)&APend - (ULONG)&APstart + 1);
1573 /* Set shutdown code */
1574 CMOS_WRITE(0xF, 0xA);
1576 /* Set warm reset vector */
1577 ps = (PUSHORT)((ULONG)BIOSBase + 0x467);
1578 *ps = (COMMON_AREA + PAGE_SIZE) & 0xF;
1580 ps = (PUSHORT)((ULONG)BIOSBase + 0x469);
1581 *ps = (COMMON_AREA + PAGE_SIZE) >> 4;
1583 /* Calibrate APIC timer */
1584 APICCalibrateTimer(0);
1586 /* The boot processor is online */
1587 OnlineCPUs = (1 << 0);
1594 HalInitializeProcessor (
1595 ULONG ProcessorNumber,
1596 PVOID ProcessorStack)
1601 PCOMMON_AREA_INFO Common;
1603 ULONG DeliveryStatus;
1608 if (ProcessorNumber == 0) {
1609 /* Boot processor is already initialized */
1614 if (NextCPU < CPUCount) {
1617 DPRINT("Attempting to boot CPU %d\n", CPU);
1620 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_ASSERT);
1625 APICSendIPI(CPUMap[CPU].APICId, APIC_DM_INIT, 0, APIC_ICR0_LEVEL_DEASSERT);
1627 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) {
1628 /* Clear APIC errors */
1629 APICWrite(APIC_ESR, 0);
1630 tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1633 Common = (PCOMMON_AREA_INFO)CommonBase;
1635 /* Write the location of the AP stack */
1636 Common->Stack = (ULONG)ProcessorStack;
1638 DPRINT("CPU %d got stack at 0x%X\n", CPU, Common->Stack);
1640 for (j = 0; j < 16; j++) {
1641 Common->Debug[j] = 0;
1645 maxlvt = APICGetMaxLVT();
1647 /* Is this a local APIC or an 82489DX? */
1648 StartupCount = (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) ? 2 : 0;
1650 for (i = 1; i <= StartupCount; i++)
1652 /* It's a local APIC, so send STARTUP IPI */
1653 DPRINT("Sending startup signal %d\n", i);
1655 APICWrite(APIC_ESR, 0);
1658 APICSendIPI(CPUMap[CPU].APICId,
1660 APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12),
1661 APIC_ICR0_LEVEL_DEASSERT);
1663 /* Wait up to 10ms for IPI to be delivered */
1668 /* Check Delivery Status */
1669 DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1672 } while ((DeliveryStatus) && (j < 1000));
1677 * Due to the Pentium erratum 3AP.
1680 APICRead(APIC_SIVR);
1681 APICWrite(APIC_ESR, 0);
1684 AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1686 if (DeliveryStatus || AcceptStatus) {
1691 if (DeliveryStatus) {
1692 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU);
1696 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU);
1699 if (!(DeliveryStatus || AcceptStatus)) {
1701 /* Wait no more than 5 seconds for processor to boot */
1702 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU);
1704 /* Wait no more than 5 seconds */
1705 for (j = 0; j < 50000; j++) {
1707 if (CPUMap[CPU].Flags & CPU_ENABLED)
1714 if (CPUMap[CPU].Flags & CPU_ENABLED) {
1715 DbgPrint("CPU %d is now running\n", CPU);
1717 DbgPrint("Initialization of CPU %d failed\n", CPU);
1721 DPRINT("Debug bytes are:\n");
1723 for (j = 0; j < 4; j++) {
1724 DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1725 Common->Debug[j*4+0],
1726 Common->Debug[j*4+1],
1727 Common->Debug[j*4+2],
1728 Common->Debug[j*4+3]);
1740 HalAllProcessorsStarted (
1747 return (NextCPU >= CPUCount);
1751 if (BSPInitialized) {
1754 BSPInitialized = TRUE;
1764 HalStartNextProcessor (
1771 /* Display the APIC registers for debugging */
1781 return (NextCPU >= CPUCount);
1795 * Checksum an MP configuration block
1811 static CHAR str[32];
1812 static PCHAR CPUs[] =
1814 "80486DX", "80486DX",
1815 "80486SX", "80486DX/2 or 80487",
1816 "80486SL", "Intel5X2(tm)",
1817 "Unknown", "Unknown",
1821 return ("Pentium(tm) Pro");
1823 return ("Pentium(tm)");
1824 if (Family == 0x0F && Model == 0x0F)
1825 return("Special controller");
1826 if (Family == 0x0F && Model == 0x00)
1827 return("Pentium 4(tm)");
1828 if (Family == 0x04 && Model < 9)
1830 sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
1835 static VOID HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
1839 if (!(m->CpuFlags & CPU_FLAG_ENABLED))
1842 DPRINT("Processor #%d %s APIC version %d\n",
1844 HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
1845 (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
1848 if (m->FeatureFlags & (1 << 0))
1849 DPRINT(" Floating point unit present.\n");
1850 if (m->FeatureFlags & (1 << 7))
1851 DPRINT(" Machine Exception supported.\n");
1852 if (m->FeatureFlags & (1 << 8))
1853 DPRINT(" 64 bit compare & exchange supported.\n");
1854 if (m->FeatureFlags & (1 << 9))
1855 DPRINT(" Internal APIC present.\n");
1856 if (m->FeatureFlags & (1 << 11))
1857 DPRINT(" SEP present.\n");
1858 if (m->FeatureFlags & (1 << 12))
1859 DPRINT(" MTRR present.\n");
1860 if (m->FeatureFlags & (1 << 13))
1861 DPRINT(" PGE present.\n");
1862 if (m->FeatureFlags & (1 << 14))
1863 DPRINT(" MCA present.\n");
1864 if (m->FeatureFlags & (1 << 15))
1865 DPRINT(" CMOV present.\n");
1866 if (m->FeatureFlags & (1 << 16))
1867 DPRINT(" PAT present.\n");
1868 if (m->FeatureFlags & (1 << 17))
1869 DPRINT(" PSE present.\n");
1870 if (m->FeatureFlags & (1 << 18))
1871 DPRINT(" PSN present.\n");
1872 if (m->FeatureFlags & (1 << 19))
1873 DPRINT(" Cache Line Flush Instruction present.\n");
1875 if (m->FeatureFlags & (1 << 21))
1876 DPRINT(" Debug Trace and EMON Store present.\n");
1877 if (m->FeatureFlags & (1 << 22))
1878 DPRINT(" ACPI Thermal Throttle Registers present.\n");
1879 if (m->FeatureFlags & (1 << 23))
1880 DPRINT(" MMX present.\n");
1881 if (m->FeatureFlags & (1 << 24))
1882 DPRINT(" FXSR present.\n");
1883 if (m->FeatureFlags & (1 << 25))
1884 DPRINT(" XMM present.\n");
1885 if (m->FeatureFlags & (1 << 26))
1886 DPRINT(" Willamette New Instructions present.\n");
1887 if (m->FeatureFlags & (1 << 27))
1888 DPRINT(" Self Snoop present.\n");
1890 if (m->FeatureFlags & (1 << 29))
1891 DPRINT(" Thermal Monitor present.\n");
1892 /* 30, 31 Reserved */
1894 CPUMap[CPUCount].APICId = m->ApicId;
1896 CPUMap[CPUCount].Flags = CPU_USABLE;
1898 if (m->CpuFlags & CPU_FLAG_BSP) {
1899 DPRINT(" Bootup CPU\n");
1900 CPUMap[CPUCount].Flags |= CPU_BSP;
1901 BootCPU = m->ApicId;
1904 if (m->ApicId > MAX_CPU) {
1905 DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
1908 ver = m->ApicVersion;
1914 DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
1917 CPUMap[CPUCount].APICVersion = ver;
1922 static VOID HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
1924 static ULONG CurrentPCIBusId = 0;
1927 memcpy(str, m->BusType, 6);
1929 DPRINT("Bus #%d is %s\n", m->BusId, str);
1931 if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) {
1932 BUSMap[m->BusId] = MP_BUS_ISA;
1933 } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) {
1934 BUSMap[m->BusId] = MP_BUS_EISA;
1935 } else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) {
1936 BUSMap[m->BusId] = MP_BUS_PCI;
1937 PCIBUSMap[m->BusId] = CurrentPCIBusId;
1939 } else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0) {
1940 BUSMap[m->BusId] = MP_BUS_MCA;
1942 DPRINT("Unknown bustype %s - ignoring\n", str);
1946 static VOID HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
1948 if (!(m->ApicFlags & CPU_FLAG_ENABLED))
1951 DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
1952 m->ApicId, m->ApicVersion, m->ApicAddress);
1953 if (IOAPICCount > MAX_IOAPIC) {
1954 DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
1955 MAX_IOAPIC, IOAPICCount);
1956 DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
1959 IOAPICMap[IOAPICCount].ApicId = m->ApicId;
1960 IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
1961 IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
1965 static VOID HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
1967 DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
1968 " IRQ %02x, APIC ID %x, APIC INT %02x\n",
1969 m->IrqType, m->IrqFlag & 3,
1970 (m->IrqFlag >> 2) & 3, m->SrcBusId,
1971 m->SrcBusIrq, m->DstApicId, m->DstApicInt);
1972 if (IRQCount > MAX_IRQ_SOURCE) {
1973 DPRINT1("Max # of irq sources exceeded!!\n");
1977 IRQMap[IRQCount] = *m;
1981 static VOID HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
1983 DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
1984 " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
1985 m->IrqType, m->IrqFlag & 3,
1986 (m->IrqFlag >> 2) & 3, m->SrcBusId,
1987 m->SrcBusIrq, m->DstApicId, m->DstApicLInt);
1989 * Well it seems all SMP boards in existence
1990 * use ExtINT/LVT1 == LINT0 and
1991 * NMI/LVT2 == LINT1 - the following check
1992 * will show us if this assumptions is false.
1993 * Until then we do not have to add baggage.
1995 if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0)) {
1996 DPRINT1("Invalid MP table!\n");
1999 if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1)) {
2000 DPRINT1("Invalid MP table!\n");
2007 HaliReadMPConfigTable(
2008 PMP_CONFIGURATION_TABLE Table)
2011 Table = Pointer to MP configuration table
2017 if (Table->Signature != MPC_SIGNATURE)
2019 PUCHAR pc = (PUCHAR)&Table->Signature;
2021 DbgPrint("Bad MP configuration block signature: %c%c%c%c\n",
2022 pc[0], pc[1], pc[2], pc[3]);
2027 if (MPChecksum((PUCHAR)Table, Table->Length))
2029 DbgPrint("Bad MP configuration block checksum\n");
2034 if (Table->Specification < 0x04)
2036 DbgPrint("Bad MP configuration table version (%d)\n",
2037 Table->Specification);
2042 APICBase = (PULONG)Table->LocalAPICAddress;
2043 if (APICBase != (PULONG)APIC_DEFAULT_BASE)
2045 DbgPrint("APIC base address is at 0x%X. " \
2046 "I cannot handle non-standard adresses\n", APICBase);
2050 Entry = (PUCHAR)((PVOID)Table + sizeof(MP_CONFIGURATION_TABLE));
2052 while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
2054 /* Switch on type */
2057 case MPCTE_PROCESSOR:
2059 HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
2060 Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
2061 Count += sizeof(MP_CONFIGURATION_PROCESSOR);
2066 HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
2067 Entry += sizeof(MP_CONFIGURATION_BUS);
2068 Count += sizeof(MP_CONFIGURATION_BUS);
2073 HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
2074 Entry += sizeof(MP_CONFIGURATION_IOAPIC);
2075 Count += sizeof(MP_CONFIGURATION_IOAPIC);
2080 HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
2081 Entry += sizeof(MP_CONFIGURATION_INTSRC);
2082 Count += sizeof(MP_CONFIGURATION_INTSRC);
2087 HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
2088 Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
2089 Count += sizeof(MP_CONFIGURATION_INTLOCAL);
2093 DbgPrint("Unknown entry in MPC table\n");
2100 static VOID HaliConstructDefaultIOIrqMPTable(
2103 MP_CONFIGURATION_INTSRC intsrc;
2106 intsrc.Type = MPCTE_INTSRC;
2107 intsrc.IrqFlag = 0; /* conforming */
2108 intsrc.SrcBusId = 0;
2109 intsrc.DstApicId = IOAPICMap[0].ApicId;
2111 intsrc.IrqType = INT_VECTORED;
2112 for (i = 0; i < 16; i++) {
2115 if (i == 0 || i == 13)
2116 continue; /* IRQ0 & IRQ13 not connected */
2120 continue; /* IRQ2 is never connected */
2123 intsrc.SrcBusIrq = i;
2124 intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
2125 HaliMPIntSrcInfo(&intsrc);
2128 intsrc.IrqType = INT_EXTINT;
2129 intsrc.SrcBusIrq = 0;
2130 intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
2131 HaliMPIntSrcInfo(&intsrc);
2135 static VOID HaliConstructDefaultISAMPTable(
2138 MP_CONFIGURATION_PROCESSOR processor;
2139 MP_CONFIGURATION_BUS bus;
2140 MP_CONFIGURATION_IOAPIC ioapic;
2141 MP_CONFIGURATION_INTLOCAL lintsrc;
2142 ULONG linttypes[2] = { INT_EXTINT, INT_NMI };
2145 APICBase = (PULONG)APIC_DEFAULT_BASE;
2148 * 2 CPUs, numbered 0 & 1.
2150 processor.Type = MPCTE_PROCESSOR;
2151 /* Either an integrated APIC or a discrete 82489DX. */
2152 processor.ApicVersion = Type > 4 ? 0x10 : 0x01;
2153 processor.CpuFlags = CPU_FLAG_ENABLED | CPU_FLAG_BSP;
2154 /* FIXME: Get this from the bootstrap processor */
2155 processor.CpuSignature = 0;
2156 processor.FeatureFlags = 0;
2157 processor.Reserved[0] = 0;
2158 processor.Reserved[1] = 0;
2159 for (i = 0; i < 2; i++) {
2160 processor.ApicId = i;
2161 HaliMPProcessorInfo(&processor);
2162 processor.CpuFlags &= ~CPU_FLAG_BSP;
2165 bus.Type = MPCTE_BUS;
2169 DPRINT("Unknown standard configuration %d\n", Type);
2173 memcpy(bus.BusType, "ISA ", 6);
2178 memcpy(bus.BusType, "EISA ", 6);
2182 memcpy(bus.BusType, "MCA ", 6);
2184 HaliMPBusInfo(&bus);
2186 bus.Type = MPCTE_BUS;
2188 memcpy(bus.BusType, "PCI ", 6);
2189 HaliMPBusInfo(&bus);
2192 ioapic.Type = MPCTE_IOAPIC;
2194 ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
2195 ioapic.ApicFlags = MP_IOAPIC_USABLE;
2196 ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
2197 HaliMPIOApicInfo(&ioapic);
2200 * We set up most of the low 16 IO-APIC pins according to MPS rules.
2202 HaliConstructDefaultIOIrqMPTable(Type);
2204 lintsrc.Type = MPCTE_LINTSRC;
2205 lintsrc.IrqType = 0;
2206 lintsrc.IrqFlag = 0; /* conforming */
2207 lintsrc.SrcBusId = 0;
2208 lintsrc.SrcBusIrq = 0;
2209 lintsrc.DstApicId = MP_APIC_ALL;
2210 for (i = 0; i < 2; i++) {
2211 lintsrc.IrqType = linttypes[i];
2212 lintsrc.DstApicLInt = i;
2213 HaliMPIntLocalInfo(&lintsrc);
2218 HaliScanForMPConfigTable(
2223 Base = Base address of region
2224 Size = Length of region to check
2226 TRUE if a valid MP configuration table was found
2229 PULONG bp = (PULONG)Base;
2230 MP_FLOATING_POINTER* mpf;
2234 if (*bp == MPF_SIGNATURE)
2236 DbgPrint("Found MPF signature at %x, checksum %x\n", bp,
2237 MPChecksum((PUCHAR)bp, 16));
2238 if (MPChecksum((PUCHAR)bp, 16) == 0)
2240 mpf = (MP_FLOATING_POINTER*)bp;
2242 DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
2243 mpf->Specification);
2245 if (mpf->Feature2 & FEATURE2_IMCRP) {
2247 DPRINT("Running in IMCR and PIC compatibility mode.\n")
2250 DPRINT("Running in Virtual Wire compatibility mode.\n");
2253 switch (mpf->Feature1)
2256 /* Non standard configuration */
2262 DPRINT("EISA with no IRQ8 chaining\n");
2271 DPRINT("ISA and PCI\n");
2274 DPRINT("EISA and PCI\n");
2277 DPRINT("MCA and PCI\n");
2280 DbgPrint("Unknown standard configuration %d\n", mpf->Feature1);
2288 if ((mpf->Feature1 == 0) && (mpf->Address)) {
2289 HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)mpf->Address);
2291 HaliConstructDefaultISAMPTable(mpf->Feature1);
2311 /* Only initialize MP system once. Once called the first time,
2312 each subsequent call is part of the initialization sequence
2313 for an application processor. */
2314 if (MPSInitialized) {
2317 DPRINT("CPU %d says it is now booted.\n", CPU);
2320 APICCalibrateTimer(CPU);
2322 /* This processor is now booted */
2323 CPUMap[CPU].Flags |= CPU_ENABLED;
2324 OnlineCPUs |= (1 << CPU);
2329 MPSInitialized = TRUE;
2332 Scan the system memory for an MP configuration table
2333 1) Scan the first KB of system base memory
2334 2) Scan the last KB of system base memory
2335 3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
2336 4) Scan the Extended BIOS Data Area
2339 if (!HaliScanForMPConfigTable(0x0, 0x400)) {
2340 if (!HaliScanForMPConfigTable(0x9FC00, 0x400)) {
2341 if (!HaliScanForMPConfigTable(0xF0000, 0x10000)) {
2342 EBDA = *((PUSHORT)0x040E);
2344 if (!HaliScanForMPConfigTable((ULONG)EBDA, 0x1000)) {
2345 DbgPrint("No multiprocessor compliant system found.\n");
2352 /* Setup IRQ to vector translation map */
2353 memset(&IRQVectorMap, sizeof(IRQVectorMap), 0);
2355 /* Initialize the bootstrap processor */
2358 /* Setup I/O APIC */
2361 /* Setup busy waiting */
2362 HalpCalibrateStallExecution();
2364 /* We can now enable interrupts */
2365 __asm__ __volatile__ ("sti\n\t");