1 /* $Id: mp.c,v 1.5 2002/12/09 18:42:41 robd Exp $
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 THREAD_STATE_RUNNABLE ???
1397 // TODO FIXME - What happened to definition for PsDispatchThread ???
1398 //PsDispatchThread(THREAD_STATE_RUNNABLE);
1404 KeLowerIrql(OldIrql
);
1409 VOID
MpsErrorHandler(
1416 tmp1
= APICRead(APIC_ESR
);
1417 APICWrite(APIC_ESR
, 0);
1418 tmp2
= APICRead(APIC_ESR
);
1421 * Acknowledge the interrupt
1425 /* Here is what the APIC error bits mean:
1428 2: Send accept error
1429 3: Receive accept error
1431 5: Send illegal vector
1432 6: Received illegal vector
1433 7: Illegal register address
1435 DPRINT1("APIC error on CPU(%d) ESR(%x)(%x)\n", ThisCPU(), tmp1
, tmp2
);
1440 VOID
MpsSpuriousHandler(
1443 DPRINT1("Spurious interrupt on CPU(%d)\n", ThisCPU());
1446 * Acknowledge the interrupt
1462 * Intel recommends to set DFR, LDR and TPR before enabling
1463 * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
1464 * document number 292116). So here it goes...
1468 * Put the APIC into flat delivery mode.
1469 * Must be "all ones" explicitly for 82489DX.
1471 APICWrite(APIC_DFR
, 0xFFFFFFFF);
1474 * Set up the logical destination ID.
1476 tmp
= APICRead(APIC_LDR
);
1477 tmp
&= ~APIC_LDR_MASK
;
1478 tmp
|= (1 << (CPU
+ 24));
1479 APICWrite(APIC_LDR
, tmp
);
1481 /* Accept all interrupts */
1482 tmp
= (APICRead(APIC_TPR
) & ~APIC_TPR_PRI
);
1483 APICWrite(APIC_TPR
, tmp
);
1485 /* Enable local APIC */
1486 tmp
= APICRead(APIC_SIVR
) | APIC_SIVR_ENABLE
| APIC_SIVR_FOCUS
; // No focus processor
1488 /* Set spurious interrupt vector */
1489 tmp
|= SPURIOUS_VECTOR
;
1491 APICWrite(APIC_SIVR
, tmp
);
1494 * Only the BP should see the LINT1 NMI signal, obviously.
1499 tmp
= APIC_DM_NMI
| APIC_LVT_MASKED
;
1500 if (!APIC_INTEGRATED(CPUMap
[CPU
].APICVersion
)) /* 82489DX */
1501 tmp
|= APIC_LVT_LEVEL_TRIGGER
;
1502 APICWrite(APIC_LINT1
, tmp
);
1504 if (APIC_INTEGRATED(CPUMap
[CPU
].APICVersion
)) { /* !82489DX */
1505 if (CPUMap
[CPU
].MaxLVT
> 3) { /* Due to the Pentium erratum 3AP */
1506 APICWrite(APIC_ESR
, 0);
1509 tmp
= APICRead(APIC_ESR
);
1510 DPRINT("ESR value before enabling vector: 0x%X\n", tmp
);
1512 /* Enable sending errors */
1514 APICWrite(APIC_LVT3
, tmp
);
1517 * Spec says clear errors after enabling vector
1519 if (CPUMap
[CPU
].MaxLVT
> 3)
1520 APICWrite(APIC_ESR
, 0);
1521 tmp
= APICRead(APIC_ESR
);
1522 DPRINT("ESR value after enabling vector: 0x%X\n", tmp
);
1532 /* Only initialize the BSP once */
1536 BSPInitialized
= TRUE
;
1538 DPRINT("APIC is mapped at 0x%X\n", APICBase
);
1540 if (VerifyLocalAPIC()) {
1541 DPRINT("APIC found\n");
1543 DPRINT1("No APIC found\n");
1547 CPUMap
[BootCPU
].MaxLVT
= APICGetMaxLVT();
1549 SetInterruptGate(LOCAL_TIMER_VECTOR
, (ULONG
)MpsTimerInterrupt
);
1550 SetInterruptGate(ERROR_VECTOR
, (ULONG
)MpsErrorInterrupt
);
1551 SetInterruptGate(SPURIOUS_VECTOR
, (ULONG
)MpsSpuriousInterrupt
);
1553 if (APICMode
== amPIC
) {
1559 /* BIOS data segment */
1560 BIOSBase
= (PULONG
)BIOS_AREA
;
1562 /* Area for communicating with the APs */
1563 CommonBase
= (PULONG
)COMMON_AREA
;
1565 /* Copy bootstrap code to common area */
1566 memcpy((PVOID
)((ULONG
)CommonBase
+ PAGE_SIZE
),
1568 (ULONG
)&APend
- (ULONG
)&APstart
+ 1);
1570 /* Set shutdown code */
1571 CMOS_WRITE(0xF, 0xA);
1573 /* Set warm reset vector */
1574 ps
= (PUSHORT
)((ULONG
)BIOSBase
+ 0x467);
1575 *ps
= (COMMON_AREA
+ PAGE_SIZE
) & 0xF;
1577 ps
= (PUSHORT
)((ULONG
)BIOSBase
+ 0x469);
1578 *ps
= (COMMON_AREA
+ PAGE_SIZE
) >> 4;
1580 /* Calibrate APIC timer */
1581 APICCalibrateTimer(0);
1583 /* The boot processor is online */
1584 OnlineCPUs
= (1 << 0);
1591 HalInitializeProcessor (
1592 ULONG ProcessorNumber
,
1593 PVOID ProcessorStack
)
1598 PCOMMON_AREA_INFO Common
;
1600 ULONG DeliveryStatus
;
1605 if (ProcessorNumber
== 0) {
1606 /* Boot processor is already initialized */
1611 if (NextCPU
< CPUCount
) {
1614 DPRINT("Attempting to boot CPU %d\n", CPU
);
1617 APICSendIPI(CPUMap
[CPU
].APICId
, APIC_DM_INIT
, 0, APIC_ICR0_LEVEL_ASSERT
);
1622 APICSendIPI(CPUMap
[CPU
].APICId
, APIC_DM_INIT
, 0, APIC_ICR0_LEVEL_DEASSERT
);
1624 if (APIC_INTEGRATED(CPUMap
[CPU
].APICVersion
)) {
1625 /* Clear APIC errors */
1626 APICWrite(APIC_ESR
, 0);
1627 tmp
= (APICRead(APIC_ESR
) & APIC_ESR_MASK
);
1630 Common
= (PCOMMON_AREA_INFO
)CommonBase
;
1632 /* Write the location of the AP stack */
1633 Common
->Stack
= (ULONG
)ProcessorStack
;
1635 DPRINT("CPU %d got stack at 0x%X\n", CPU
, Common
->Stack
);
1637 for (j
= 0; j
< 16; j
++) {
1638 Common
->Debug
[j
] = 0;
1642 maxlvt
= APICGetMaxLVT();
1644 /* Is this a local APIC or an 82489DX? */
1645 StartupCount
= (APIC_INTEGRATED(CPUMap
[CPU
].APICVersion
)) ? 2 : 0;
1647 for (i
= 1; i
<= StartupCount
; i
++)
1649 /* It's a local APIC, so send STARTUP IPI */
1650 DPRINT("Sending startup signal %d\n", i
);
1652 APICWrite(APIC_ESR
, 0);
1655 APICSendIPI(CPUMap
[CPU
].APICId
,
1657 APIC_DM_STARTUP
| ((COMMON_AREA
+ PAGE_SIZE
) >> 12),
1658 APIC_ICR0_LEVEL_DEASSERT
);
1660 /* Wait up to 10ms for IPI to be delivered */
1665 /* Check Delivery Status */
1666 DeliveryStatus
= APICRead(APIC_ICR0
) & APIC_ICR0_DS
;
1669 } while ((DeliveryStatus
) && (j
< 1000));
1674 * Due to the Pentium erratum 3AP.
1677 APICRead(APIC_SIVR
);
1678 APICWrite(APIC_ESR
, 0);
1681 AcceptStatus
= APICRead(APIC_ESR
) & APIC_ESR_MASK
;
1683 if (DeliveryStatus
|| AcceptStatus
) {
1688 if (DeliveryStatus
) {
1689 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU
);
1693 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU
);
1696 if (!(DeliveryStatus
|| AcceptStatus
)) {
1698 /* Wait no more than 5 seconds for processor to boot */
1699 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU
);
1701 /* Wait no more than 5 seconds */
1702 for (j
= 0; j
< 50000; j
++) {
1704 if (CPUMap
[CPU
].Flags
& CPU_ENABLED
)
1711 if (CPUMap
[CPU
].Flags
& CPU_ENABLED
) {
1712 DbgPrint("CPU %d is now running\n", CPU
);
1714 DbgPrint("Initialization of CPU %d failed\n", CPU
);
1718 DPRINT("Debug bytes are:\n");
1720 for (j
= 0; j
< 4; j
++) {
1721 DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1722 Common
->Debug
[j
*4+0],
1723 Common
->Debug
[j
*4+1],
1724 Common
->Debug
[j
*4+2],
1725 Common
->Debug
[j
*4+3]);
1737 HalAllProcessorsStarted (
1744 return (NextCPU
>= CPUCount
);
1748 if (BSPInitialized
) {
1751 BSPInitialized
= TRUE
;
1761 HalStartNextProcessor (
1768 /* Display the APIC registers for debugging */
1778 return (NextCPU
>= CPUCount
);
1792 * Checksum an MP configuration block
1808 static CHAR str
[32];
1809 static PCHAR CPUs
[] =
1811 "80486DX", "80486DX",
1812 "80486SX", "80486DX/2 or 80487",
1813 "80486SL", "Intel5X2(tm)",
1814 "Unknown", "Unknown",
1818 return ("Pentium(tm) Pro");
1820 return ("Pentium(tm)");
1821 if (Family
== 0x0F && Model
== 0x0F)
1822 return("Special controller");
1823 if (Family
== 0x0F && Model
== 0x00)
1824 return("Pentium 4(tm)");
1825 if (Family
== 0x04 && Model
< 9)
1827 sprintf(str
, "Unknown CPU with family ID %ld and model ID %ld", Family
, Model
);
1832 static VOID
HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m
)
1836 if (!(m
->CpuFlags
& CPU_FLAG_ENABLED
))
1839 DPRINT("Processor #%d %s APIC version %d\n",
1841 HaliMPFamily((m
->FeatureFlags
& CPU_FAMILY_MASK
) >> 8,
1842 (m
->FeatureFlags
& CPU_MODEL_MASK
) >> 4),
1845 if (m
->FeatureFlags
& (1 << 0))
1846 DPRINT(" Floating point unit present.\n");
1847 if (m
->FeatureFlags
& (1 << 7))
1848 DPRINT(" Machine Exception supported.\n");
1849 if (m
->FeatureFlags
& (1 << 8))
1850 DPRINT(" 64 bit compare & exchange supported.\n");
1851 if (m
->FeatureFlags
& (1 << 9))
1852 DPRINT(" Internal APIC present.\n");
1853 if (m
->FeatureFlags
& (1 << 11))
1854 DPRINT(" SEP present.\n");
1855 if (m
->FeatureFlags
& (1 << 12))
1856 DPRINT(" MTRR present.\n");
1857 if (m
->FeatureFlags
& (1 << 13))
1858 DPRINT(" PGE present.\n");
1859 if (m
->FeatureFlags
& (1 << 14))
1860 DPRINT(" MCA present.\n");
1861 if (m
->FeatureFlags
& (1 << 15))
1862 DPRINT(" CMOV present.\n");
1863 if (m
->FeatureFlags
& (1 << 16))
1864 DPRINT(" PAT present.\n");
1865 if (m
->FeatureFlags
& (1 << 17))
1866 DPRINT(" PSE present.\n");
1867 if (m
->FeatureFlags
& (1 << 18))
1868 DPRINT(" PSN present.\n");
1869 if (m
->FeatureFlags
& (1 << 19))
1870 DPRINT(" Cache Line Flush Instruction present.\n");
1872 if (m
->FeatureFlags
& (1 << 21))
1873 DPRINT(" Debug Trace and EMON Store present.\n");
1874 if (m
->FeatureFlags
& (1 << 22))
1875 DPRINT(" ACPI Thermal Throttle Registers present.\n");
1876 if (m
->FeatureFlags
& (1 << 23))
1877 DPRINT(" MMX present.\n");
1878 if (m
->FeatureFlags
& (1 << 24))
1879 DPRINT(" FXSR present.\n");
1880 if (m
->FeatureFlags
& (1 << 25))
1881 DPRINT(" XMM present.\n");
1882 if (m
->FeatureFlags
& (1 << 26))
1883 DPRINT(" Willamette New Instructions present.\n");
1884 if (m
->FeatureFlags
& (1 << 27))
1885 DPRINT(" Self Snoop present.\n");
1887 if (m
->FeatureFlags
& (1 << 29))
1888 DPRINT(" Thermal Monitor present.\n");
1889 /* 30, 31 Reserved */
1891 CPUMap
[CPUCount
].APICId
= m
->ApicId
;
1893 CPUMap
[CPUCount
].Flags
= CPU_USABLE
;
1895 if (m
->CpuFlags
& CPU_FLAG_BSP
) {
1896 DPRINT(" Bootup CPU\n");
1897 CPUMap
[CPUCount
].Flags
|= CPU_BSP
;
1898 BootCPU
= m
->ApicId
;
1901 if (m
->ApicId
> MAX_CPU
) {
1902 DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m
->ApicId
, MAX_CPU
);
1905 ver
= m
->ApicVersion
;
1911 DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m
->ApicId
);
1914 CPUMap
[CPUCount
].APICVersion
= ver
;
1919 static VOID
HaliMPBusInfo(PMP_CONFIGURATION_BUS m
)
1921 static ULONG CurrentPCIBusId
= 0;
1924 memcpy(str
, m
->BusType
, 6);
1926 DPRINT("Bus #%d is %s\n", m
->BusId
, str
);
1928 if (strncmp(str
, BUSTYPE_ISA
, sizeof(BUSTYPE_ISA
)-1) == 0) {
1929 BUSMap
[m
->BusId
] = MP_BUS_ISA
;
1930 } else if (strncmp(str
, BUSTYPE_EISA
, sizeof(BUSTYPE_EISA
)-1) == 0) {
1931 BUSMap
[m
->BusId
] = MP_BUS_EISA
;
1932 } else if (strncmp(str
, BUSTYPE_PCI
, sizeof(BUSTYPE_PCI
)-1) == 0) {
1933 BUSMap
[m
->BusId
] = MP_BUS_PCI
;
1934 PCIBUSMap
[m
->BusId
] = CurrentPCIBusId
;
1936 } else if (strncmp(str
, BUSTYPE_MCA
, sizeof(BUSTYPE_MCA
)-1) == 0) {
1937 BUSMap
[m
->BusId
] = MP_BUS_MCA
;
1939 DPRINT("Unknown bustype %s - ignoring\n", str
);
1943 static VOID
HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m
)
1945 if (!(m
->ApicFlags
& CPU_FLAG_ENABLED
))
1948 DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
1949 m
->ApicId
, m
->ApicVersion
, m
->ApicAddress
);
1950 if (IOAPICCount
> MAX_IOAPIC
) {
1951 DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
1952 MAX_IOAPIC
, IOAPICCount
);
1953 DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
1956 IOAPICMap
[IOAPICCount
].ApicId
= m
->ApicId
;
1957 IOAPICMap
[IOAPICCount
].ApicVersion
= m
->ApicVersion
;
1958 IOAPICMap
[IOAPICCount
].ApicAddress
= m
->ApicAddress
;
1962 static VOID
HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m
)
1964 DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
1965 " IRQ %02x, APIC ID %x, APIC INT %02x\n",
1966 m
->IrqType
, m
->IrqFlag
& 3,
1967 (m
->IrqFlag
>> 2) & 3, m
->SrcBusId
,
1968 m
->SrcBusIrq
, m
->DstApicId
, m
->DstApicInt
);
1969 if (IRQCount
> MAX_IRQ_SOURCE
) {
1970 DPRINT1("Max # of irq sources exceeded!!\n");
1974 IRQMap
[IRQCount
] = *m
;
1978 static VOID
HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m
)
1980 DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
1981 " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
1982 m
->IrqType
, m
->IrqFlag
& 3,
1983 (m
->IrqFlag
>> 2) & 3, m
->SrcBusId
,
1984 m
->SrcBusIrq
, m
->DstApicId
, m
->DstApicLInt
);
1986 * Well it seems all SMP boards in existence
1987 * use ExtINT/LVT1 == LINT0 and
1988 * NMI/LVT2 == LINT1 - the following check
1989 * will show us if this assumptions is false.
1990 * Until then we do not have to add baggage.
1992 if ((m
->IrqType
== INT_EXTINT
) && (m
->DstApicLInt
!= 0)) {
1993 DPRINT1("Invalid MP table!\n");
1996 if ((m
->IrqType
== INT_NMI
) && (m
->DstApicLInt
!= 1)) {
1997 DPRINT1("Invalid MP table!\n");
2004 HaliReadMPConfigTable(
2005 PMP_CONFIGURATION_TABLE Table
)
2008 Table = Pointer to MP configuration table
2014 if (Table
->Signature
!= MPC_SIGNATURE
)
2016 PUCHAR pc
= (PUCHAR
)&Table
->Signature
;
2018 DbgPrint("Bad MP configuration block signature: %c%c%c%c\n",
2019 pc
[0], pc
[1], pc
[2], pc
[3]);
2024 if (MPChecksum((PUCHAR
)Table
, Table
->Length
))
2026 DbgPrint("Bad MP configuration block checksum\n");
2031 if (Table
->Specification
< 0x04)
2033 DbgPrint("Bad MP configuration table version (%d)\n",
2034 Table
->Specification
);
2039 APICBase
= (PULONG
)Table
->LocalAPICAddress
;
2040 if (APICBase
!= (PULONG
)APIC_DEFAULT_BASE
)
2042 DbgPrint("APIC base address is at 0x%X. " \
2043 "I cannot handle non-standard adresses\n", APICBase
);
2047 Entry
= (PUCHAR
)((PVOID
)Table
+ sizeof(MP_CONFIGURATION_TABLE
));
2049 while (Count
< (Table
->Length
- sizeof(MP_CONFIGURATION_TABLE
)))
2051 /* Switch on type */
2054 case MPCTE_PROCESSOR
:
2056 HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR
)Entry
);
2057 Entry
+= sizeof(MP_CONFIGURATION_PROCESSOR
);
2058 Count
+= sizeof(MP_CONFIGURATION_PROCESSOR
);
2063 HaliMPBusInfo((PMP_CONFIGURATION_BUS
)Entry
);
2064 Entry
+= sizeof(MP_CONFIGURATION_BUS
);
2065 Count
+= sizeof(MP_CONFIGURATION_BUS
);
2070 HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC
)Entry
);
2071 Entry
+= sizeof(MP_CONFIGURATION_IOAPIC
);
2072 Count
+= sizeof(MP_CONFIGURATION_IOAPIC
);
2077 HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC
)Entry
);
2078 Entry
+= sizeof(MP_CONFIGURATION_INTSRC
);
2079 Count
+= sizeof(MP_CONFIGURATION_INTSRC
);
2084 HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL
)Entry
);
2085 Entry
+= sizeof(MP_CONFIGURATION_INTLOCAL
);
2086 Count
+= sizeof(MP_CONFIGURATION_INTLOCAL
);
2090 DbgPrint("Unknown entry in MPC table\n");
2097 static VOID
HaliConstructDefaultIOIrqMPTable(
2100 MP_CONFIGURATION_INTSRC intsrc
;
2103 intsrc
.Type
= MPCTE_INTSRC
;
2104 intsrc
.IrqFlag
= 0; /* conforming */
2105 intsrc
.SrcBusId
= 0;
2106 intsrc
.DstApicId
= IOAPICMap
[0].ApicId
;
2108 intsrc
.IrqType
= INT_VECTORED
;
2109 for (i
= 0; i
< 16; i
++) {
2112 if (i
== 0 || i
== 13)
2113 continue; /* IRQ0 & IRQ13 not connected */
2117 continue; /* IRQ2 is never connected */
2120 intsrc
.SrcBusIrq
= i
;
2121 intsrc
.DstApicInt
= i
? i
: 2; /* IRQ0 to INTIN2 */
2122 HaliMPIntSrcInfo(&intsrc
);
2125 intsrc
.IrqType
= INT_EXTINT
;
2126 intsrc
.SrcBusIrq
= 0;
2127 intsrc
.DstApicInt
= 0; /* 8259A to INTIN0 */
2128 HaliMPIntSrcInfo(&intsrc
);
2132 static VOID
HaliConstructDefaultISAMPTable(
2135 MP_CONFIGURATION_PROCESSOR processor
;
2136 MP_CONFIGURATION_BUS bus
;
2137 MP_CONFIGURATION_IOAPIC ioapic
;
2138 MP_CONFIGURATION_INTLOCAL lintsrc
;
2139 ULONG linttypes
[2] = { INT_EXTINT
, INT_NMI
};
2142 APICBase
= (PULONG
)APIC_DEFAULT_BASE
;
2145 * 2 CPUs, numbered 0 & 1.
2147 processor
.Type
= MPCTE_PROCESSOR
;
2148 /* Either an integrated APIC or a discrete 82489DX. */
2149 processor
.ApicVersion
= Type
> 4 ? 0x10 : 0x01;
2150 processor
.CpuFlags
= CPU_FLAG_ENABLED
| CPU_FLAG_BSP
;
2151 /* FIXME: Get this from the bootstrap processor */
2152 processor
.CpuSignature
= 0;
2153 processor
.FeatureFlags
= 0;
2154 processor
.Reserved
[0] = 0;
2155 processor
.Reserved
[1] = 0;
2156 for (i
= 0; i
< 2; i
++) {
2157 processor
.ApicId
= i
;
2158 HaliMPProcessorInfo(&processor
);
2159 processor
.CpuFlags
&= ~CPU_FLAG_BSP
;
2162 bus
.Type
= MPCTE_BUS
;
2166 DPRINT("Unknown standard configuration %d\n", Type
);
2170 memcpy(bus
.BusType
, "ISA ", 6);
2175 memcpy(bus
.BusType
, "EISA ", 6);
2179 memcpy(bus
.BusType
, "MCA ", 6);
2181 HaliMPBusInfo(&bus
);
2183 bus
.Type
= MPCTE_BUS
;
2185 memcpy(bus
.BusType
, "PCI ", 6);
2186 HaliMPBusInfo(&bus
);
2189 ioapic
.Type
= MPCTE_IOAPIC
;
2191 ioapic
.ApicVersion
= Type
> 4 ? 0x10 : 0x01;
2192 ioapic
.ApicFlags
= MP_IOAPIC_USABLE
;
2193 ioapic
.ApicAddress
= IOAPIC_DEFAULT_BASE
;
2194 HaliMPIOApicInfo(&ioapic
);
2197 * We set up most of the low 16 IO-APIC pins according to MPS rules.
2199 HaliConstructDefaultIOIrqMPTable(Type
);
2201 lintsrc
.Type
= MPCTE_LINTSRC
;
2202 lintsrc
.IrqType
= 0;
2203 lintsrc
.IrqFlag
= 0; /* conforming */
2204 lintsrc
.SrcBusId
= 0;
2205 lintsrc
.SrcBusIrq
= 0;
2206 lintsrc
.DstApicId
= MP_APIC_ALL
;
2207 for (i
= 0; i
< 2; i
++) {
2208 lintsrc
.IrqType
= linttypes
[i
];
2209 lintsrc
.DstApicLInt
= i
;
2210 HaliMPIntLocalInfo(&lintsrc
);
2215 HaliScanForMPConfigTable(
2220 Base = Base address of region
2221 Size = Length of region to check
2223 TRUE if a valid MP configuration table was found
2226 PULONG bp
= (PULONG
)Base
;
2227 MP_FLOATING_POINTER
* mpf
;
2231 if (*bp
== MPF_SIGNATURE
)
2233 DbgPrint("Found MPF signature at %x, checksum %x\n", bp
,
2234 MPChecksum((PUCHAR
)bp
, 16));
2235 if (MPChecksum((PUCHAR
)bp
, 16) == 0)
2237 mpf
= (MP_FLOATING_POINTER
*)bp
;
2239 DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
2240 mpf
->Specification
);
2242 if (mpf
->Feature2
& FEATURE2_IMCRP
) {
2244 DPRINT("Running in IMCR and PIC compatibility mode.\n")
2247 DPRINT("Running in Virtual Wire compatibility mode.\n");
2250 switch (mpf
->Feature1
)
2253 /* Non standard configuration */
2259 DPRINT("EISA with no IRQ8 chaining\n");
2268 DPRINT("ISA and PCI\n");
2271 DPRINT("EISA and PCI\n");
2274 DPRINT("MCA and PCI\n");
2277 DbgPrint("Unknown standard configuration %d\n", mpf
->Feature1
);
2285 if ((mpf
->Feature1
== 0) && (mpf
->Address
)) {
2286 HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE
)mpf
->Address
);
2288 HaliConstructDefaultISAMPTable(mpf
->Feature1
);
2308 /* Only initialize MP system once. Once called the first time,
2309 each subsequent call is part of the initialization sequence
2310 for an application processor. */
2311 if (MPSInitialized
) {
2314 DPRINT("CPU %d says it is now booted.\n", CPU
);
2317 APICCalibrateTimer(CPU
);
2319 /* This processor is now booted */
2320 CPUMap
[CPU
].Flags
|= CPU_ENABLED
;
2321 OnlineCPUs
|= (1 << CPU
);
2326 MPSInitialized
= TRUE
;
2329 Scan the system memory for an MP configuration table
2330 1) Scan the first KB of system base memory
2331 2) Scan the last KB of system base memory
2332 3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
2333 4) Scan the Extended BIOS Data Area
2336 if (!HaliScanForMPConfigTable(0x0, 0x400)) {
2337 if (!HaliScanForMPConfigTable(0x9FC00, 0x400)) {
2338 if (!HaliScanForMPConfigTable(0xF0000, 0x10000)) {
2339 EBDA
= *((PUSHORT
)0x040E);
2341 if (!HaliScanForMPConfigTable((ULONG
)EBDA
, 0x1000)) {
2342 DbgPrint("No multiprocessor compliant system found.\n");
2349 /* Setup IRQ to vector translation map */
2350 memset(&IRQVectorMap
, sizeof(IRQVectorMap
), 0);
2352 /* Initialize the bootstrap processor */
2355 /* Setup I/O APIC */
2358 /* Setup busy waiting */
2359 HalpCalibrateStallExecution();
2361 /* We can now enable interrupts */
2362 __asm__
__volatile__ ("sti\n\t");