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