8c1add04ea361a74a6614b65e411c6bd9cb007d7
[reactos.git] / reactos / hal / halx86 / mp.c
1 /* $Id: mp.c,v 1.12 2004/11/28 01:30:01 hbirr 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 <roscfg.h>
18 #include <ddk/ntddk.h>
19
20
21 #ifdef MP
22
23 #include <hal.h>
24 #include <halirq.h>
25 #include <mps.h>
26 #include <apic.h>
27
28 #include <internal/ntoskrnl.h>
29 #include <internal/i386/segment.h>
30 #include <internal/ke.h>
31 #include <internal/ps.h>
32
33 #endif
34
35 #define NDEBUG
36 #include <internal/debug.h>
37
38 #ifdef MP
39
40
41 /*
42 Address of area to be used for communication between Application
43 Processors (APs) and the BootStrap Processor (BSP)
44 */
45 #define COMMON_AREA 0x2000
46
47 #define BIOS_AREA 0x0
48
49 typedef struct __attribute__((packed)) _COMMON_AREA_INFO
50 {
51 ULONG Stack; /* Location of AP stack */
52 ULONG Debug[16]; /* For debugging */
53 } COMMON_AREA_INFO, *PCOMMON_AREA_INFO;
54
55 CPU_INFO CPUMap[MAX_CPU]; /* Map of all CPUs in the system */
56 ULONG CPUCount; /* Total number of CPUs */
57 ULONG OnlineCPUs; /* Bitmask of online CPUs */
58
59 UCHAR BUSMap[MAX_BUS]; /* Map of all buses in the system */
60 UCHAR PCIBUSMap[MAX_BUS]; /* Map of all PCI buses in the system */
61
62 IOAPIC_INFO IOAPICMap[MAX_IOAPIC]; /* Map of all I/O APICs in the system */
63 ULONG IOAPICCount; /* Number of I/O APICs in the system */
64
65 MP_CONFIGURATION_INTSRC IRQMap[MAX_IRQ_SOURCE]; /* Map of all IRQs */
66 ULONG IRQVectorMap[MAX_IRQ_SOURCE]; /* IRQ to vector map */
67 ULONG IrqPinMap[MAX_IRQ_SOURCE]; /* IRQ to Pin map */
68 ULONG IrqApicMap[MAX_IRQ_SOURCE];
69 ULONG IRQCount; /* Number of IRQs */
70
71 ULONG APICMode; /* APIC mode at startup */
72 ULONG BootCPU; /* Bootstrap processor */
73 PULONG BIOSBase; /* Virtual address of BIOS data segment */
74 PULONG CommonBase; /* Virtual address of common area */
75
76 extern CHAR *APstart, *APend;
77 extern VOID (*APflush)(VOID);
78
79 extern VOID MpsTimerInterrupt(VOID);
80 extern VOID MpsErrorInterrupt(VOID);
81 extern VOID MpsSpuriousInterrupt(VOID);
82 extern VOID MpsIpiInterrupt(VOID);
83
84 #define CMOS_READ(address) ({ \
85 WRITE_PORT_UCHAR((PUCHAR)0x70, address)); \
86 READ_PORT_UCHAR((PUCHAR)0x71)); \
87 })
88
89 #define CMOS_WRITE(address, value) ({ \
90 WRITE_PORT_UCHAR((PUCHAR)0x70, address); \
91 WRITE_PORT_UCHAR((PUCHAR)0x71, value); \
92 })
93
94 #endif /* MP */
95
96
97
98 /* FUNCTIONS *****************************************************************/
99
100 #ifdef MP
101
102 /* Functions for handling 8259A PICs */
103
104 VOID Disable8259AIrq(ULONG irq)
105 {
106 ULONG tmp;
107
108 if (irq >= 8)
109 {
110 tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
111 tmp |= (1 << (irq - 8));
112 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
113 }
114 else
115 {
116 tmp = READ_PORT_UCHAR((PUCHAR)0x21);
117 tmp |= (1 << irq);
118 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
119 }
120 }
121
122
123 VOID Enable8259AIrq(ULONG irq)
124 {
125 ULONG tmp;
126
127 if (irq >= 8)
128 {
129 tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
130 tmp &= ~(1 << (irq - 8));
131 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
132 }
133 else
134 {
135 tmp = READ_PORT_UCHAR((PUCHAR)0x21);
136 tmp &= ~(1 << irq);
137 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
138 }
139 }
140
141
142 /* Functions for handling I/O APICs */
143
144 volatile ULONG IOAPICRead(ULONG Apic, ULONG Offset)
145 {
146 PULONG Base;
147
148 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
149 *Base = Offset;
150 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN));
151 }
152
153 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value)
154 {
155 PULONG Base;
156
157 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
158 *Base = Offset;
159 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value;
160 }
161
162
163 VOID IOAPICClearPin(ULONG Apic, ULONG Pin)
164 {
165 IOAPIC_ROUTE_ENTRY Entry;
166
167 DPRINT("IOAPICClearPin(Apic %d, Pin %d\n", Apic, Pin);
168 /*
169 * Disable it in the IO-APIC irq-routing table
170 */
171 memset(&Entry, 0, sizeof(Entry));
172 Entry.mask = 1;
173
174 IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0));
175 IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1));
176 }
177
178 static VOID IOAPICClear(ULONG Apic)
179 {
180 ULONG Pin;
181
182 for (Pin = 0; Pin < /*IOAPICMap[Apic].EntryCount*/24; Pin++)
183 {
184 IOAPICClearPin(Apic, Pin);
185 }
186 }
187
188 static VOID IOAPICClearAll(VOID)
189 {
190 ULONG Apic;
191
192 for (Apic = 0; Apic < IOAPICCount; Apic++)
193 {
194 IOAPICClear(Apic);
195 }
196 }
197
198 /* This is performance critical and should probably be done in assembler */
199 VOID IOAPICMaskIrq(ULONG Irq)
200 {
201 IOAPIC_ROUTE_ENTRY Entry;
202 ULONG Apic = IrqApicMap[Irq];
203
204
205 *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
206 Entry.mask = 1;
207 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *((PULONG)&Entry));
208 }
209
210
211 /* This is performance critical and should probably be done in assembler */
212 VOID IOAPICUnmaskIrq(ULONG Irq)
213 {
214 IOAPIC_ROUTE_ENTRY Entry;
215 ULONG Apic = IrqApicMap[Irq];
216
217 *((PULONG)&Entry) = IOAPICRead(Apic, IOAPIC_REDTBL+2*IrqPinMap[Irq]);
218 Entry.mask = 0;
219 IOAPICWrite(Apic, IOAPIC_REDTBL+2*IrqPinMap[Irq], *((PULONG)&Entry));
220 }
221
222 static VOID
223 IOAPICSetupIds(VOID)
224 {
225 ULONG tmp, apic, i;
226 UCHAR old_id;
227
228 /*
229 * Set the IOAPIC ID to the value stored in the MPC table.
230 */
231 for (apic = 0; apic < IOAPICCount; apic++)
232 {
233
234 /* Read the register 0 value */
235 tmp = IOAPICRead(apic, IOAPIC_ID);
236
237 old_id = IOAPICMap[apic].ApicId;
238
239 if (IOAPICMap[apic].ApicId >= 0xf)
240 {
241 DPRINT1("BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",
242 apic, IOAPICMap[apic].ApicId);
243 DPRINT1("... fixing up to %d. (tell your hw vendor)\n",
244 GET_IOAPIC_ID(tmp));
245 IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp);
246 }
247
248 /*
249 * We need to adjust the IRQ routing table
250 * if the ID changed.
251 */
252 if (old_id != IOAPICMap[apic].ApicId)
253 {
254 for (i = 0; i < IRQCount; i++)
255 {
256 if (IRQMap[i].DstApicId == old_id)
257 {
258 IRQMap[i].DstApicId = IOAPICMap[apic].ApicId;
259 }
260 }
261 }
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 {
281 DPRINT1("Could not set I/O APIC ID!\n");
282 KEBUGCHECK(0);
283 }
284 }
285 }
286
287
288 /*
289 * EISA Edge/Level control register, ELCR
290 */
291 static ULONG EISA_ELCR(ULONG irq)
292 {
293 if (irq < 16)
294 {
295 PUCHAR port = (PUCHAR)(0x4d0 + (irq >> 3));
296 return (READ_PORT_UCHAR(port) >> (irq & 7)) & 1;
297 }
298 DPRINT("Broken MPtable reports ISA irq %d\n", irq);
299 return 0;
300 }
301
302 /* EISA interrupts are always polarity zero and can be edge or level
303 * trigger depending on the ELCR value. If an interrupt is listed as
304 * EISA conforming in the MP table, that means its trigger type must
305 * be read in from the ELCR */
306
307 #define default_EISA_trigger(idx) (EISA_ELCR(IRQMap[idx].SrcBusIrq))
308 #define default_EISA_polarity(idx) (0)
309
310 /* ISA interrupts are always polarity zero edge triggered,
311 * when listed as conforming in the MP table. */
312
313 #define default_ISA_trigger(idx) (0)
314 #define default_ISA_polarity(idx) (0)
315
316 /* PCI interrupts are always polarity one level triggered,
317 * when listed as conforming in the MP table. */
318
319 #define default_PCI_trigger(idx) (1)
320 #define default_PCI_polarity(idx) (1)
321
322 /* MCA interrupts are always polarity zero level triggered,
323 * when listed as conforming in the MP table. */
324
325 #define default_MCA_trigger(idx) (1)
326 #define default_MCA_polarity(idx) (0)
327
328 static ULONG IRQPolarity(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(ULONG idx)
398 {
399 ULONG bus = IRQMap[idx].SrcBusId;
400 ULONG trigger;
401
402 /*
403 * Determine IRQ trigger mode (edge or level sensitive):
404 */
405 switch ((IRQMap[idx].IrqFlag >> 2) & 3)
406 {
407 case 0: /* conforms, ie. bus-type dependent */
408 {
409 switch (BUSMap[bus])
410 {
411 case MP_BUS_ISA: /* ISA pin */
412 {
413 trigger = default_ISA_trigger(idx);
414 break;
415 }
416 case MP_BUS_EISA: /* EISA pin */
417 {
418 trigger = default_EISA_trigger(idx);
419 break;
420 }
421 case MP_BUS_PCI: /* PCI pin */
422 {
423 trigger = default_PCI_trigger(idx);
424 break;
425 }
426 case MP_BUS_MCA: /* MCA pin */
427 {
428 trigger = default_MCA_trigger(idx);
429 break;
430 }
431 default:
432 {
433 DPRINT("Broken BIOS!!\n");
434 trigger = 1;
435 break;
436 }
437 }
438 break;
439 }
440 case 1: /* edge */
441 {
442 trigger = 0;
443 break;
444 }
445 case 2: /* reserved */
446 {
447 DPRINT("Broken BIOS!!\n");
448 trigger = 1;
449 break;
450 }
451 case 3: /* level */
452 {
453 trigger = 1;
454 break;
455 }
456 default: /* invalid */
457 {
458 DPRINT("Broken BIOS!!\n");
459 trigger = 0;
460 break;
461 }
462 }
463 return trigger;
464 }
465
466
467 static ULONG Pin2Irq(ULONG idx,
468 ULONG apic,
469 ULONG pin)
470 {
471 ULONG irq, i;
472 ULONG bus = IRQMap[idx].SrcBusId;
473
474 /*
475 * Debugging check, we are in big trouble if this message pops up!
476 */
477 if (IRQMap[idx].DstApicInt != pin) {
478 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\n");
479 }
480
481 switch (BUSMap[bus])
482 {
483 case MP_BUS_ISA: /* ISA pin */
484 case MP_BUS_EISA:
485 case MP_BUS_MCA:
486 {
487 irq = IRQMap[idx].SrcBusIrq;
488 break;
489 }
490 case MP_BUS_PCI: /* PCI pin */
491 {
492 /*
493 * PCI IRQs are mapped in order
494 */
495 i = irq = 0;
496 while (i < apic)
497 irq += IOAPICMap[i++].EntryCount;
498 irq += pin;
499 break;
500 }
501 default:
502 {
503 DPRINT("Unknown bus type %d.\n",bus);
504 irq = 0;
505 break;
506 }
507 }
508
509 return irq;
510 }
511
512
513 /*
514 * Rough estimation of how many shared IRQs there are, can
515 * be changed anytime.
516 */
517 #define MAX_PLUS_SHARED_IRQS PIC_IRQS
518 #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + PIC_IRQS)
519
520 /*
521 * This is performance-critical, we want to do it O(1)
522 *
523 * the indexing order of this array favors 1:1 mappings
524 * between pins and IRQs.
525 */
526
527 static struct irq_pin_list {
528 ULONG apic, pin, next;
529 } irq_2_pin[PIN_MAP_SIZE];
530
531 /*
532 * The common case is 1:1 IRQ<->pin mappings. Sometimes there are
533 * shared ISA-space IRQs, so we have to support them. We are super
534 * fast in the common case, and fast for shared ISA-space IRQs.
535 */
536 static VOID AddPinToIrq(ULONG irq,
537 ULONG apic,
538 ULONG pin)
539 {
540 static ULONG first_free_entry = PIC_IRQS;
541 struct irq_pin_list *entry = irq_2_pin + irq;
542
543 while (entry->next)
544 {
545 entry = irq_2_pin + entry->next;
546 }
547
548 if (entry->pin != -1)
549 {
550 entry->next = first_free_entry;
551 entry = irq_2_pin + entry->next;
552 if (++first_free_entry >= PIN_MAP_SIZE)
553 {
554 DPRINT1("Ohh no!");
555 KEBUGCHECK(0);
556 }
557 }
558 entry->apic = apic;
559 entry->pin = pin;
560 }
561
562
563 /*
564 * Find the IRQ entry number of a certain pin.
565 */
566 static ULONG IOAPICGetIrqEntry(ULONG apic,
567 ULONG pin,
568 ULONG type)
569 {
570 ULONG i;
571
572 for (i = 0; i < IRQCount; i++)
573 {
574 if (IRQMap[i].IrqType == type &&
575 (IRQMap[i].DstApicId == IOAPICMap[apic].ApicId || IRQMap[i].DstApicId == MP_APIC_ALL) &&
576 IRQMap[i].DstApicInt == pin)
577 {
578 return i;
579 }
580 }
581 return -1;
582 }
583
584
585 static ULONG AssignIrqVector(ULONG irq)
586 {
587 #if 0
588 static ULONG current_vector = FIRST_DEVICE_VECTOR, vector_offset = 0;
589 #endif
590 ULONG vector;
591 /* There may already have been assigned a vector for this IRQ */
592 vector = IRQVectorMap[irq];
593 if (vector > 0)
594 {
595 return vector;
596 }
597 #if 0
598 if (current_vector > FIRST_SYSTEM_VECTOR) {
599 vector_offset++;
600 current_vector = FIRST_DEVICE_VECTOR + vector_offset;
601 } else if (current_vector == FIRST_SYSTEM_VECTOR) {
602 DPRINT1("Ran out of interrupt sources!");
603 KEBUGCHECK(0);
604 }
605
606 vector = current_vector;
607 IRQVectorMap[irq] = vector;
608 current_vector += 8;
609 return vector;
610 #else
611 vector = IRQ2VECTOR(irq);
612 IRQVectorMap[irq] = vector;
613 return vector;
614 #endif
615 }
616
617
618 VOID IOAPICSetupIrqs(VOID)
619 {
620 IOAPIC_ROUTE_ENTRY entry;
621 ULONG apic, pin, idx, irq, first_notcon = 1, vector;
622
623 DPRINT("Init IO_APIC IRQs\n");
624
625 for (apic = 0; apic < IOAPICCount; apic++)
626 {
627 for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++)
628 {
629 /*
630 * add it to the IO-APIC irq-routing table
631 */
632 memset(&entry,0,sizeof(entry));
633
634 entry.delivery_mode = APIC_DM_LOWEST;
635 entry.dest_mode = 1; /* logical delivery */
636 entry.mask = 1; /* disable IRQ */
637 #if 0
638 /*
639 * FIXME:
640 * Some drivers are not able to deal with more than one cpu.
641 */
642 entry.dest.logical.logical_dest = OnlineCPUs;
643 #else
644 entry.dest.logical.logical_dest = 1 << 0;
645 #endif
646 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED);
647 if (idx == -1)
648 {
649 if (first_notcon)
650 {
651 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap[apic].ApicId, pin);
652 first_notcon = 0;
653 }
654 else
655 {
656 DPRINT(", %d-%d\n", IOAPICMap[apic].ApicId, pin);
657 }
658 continue;
659 }
660
661 entry.trigger = IRQTrigger(idx);
662 entry.polarity = IRQPolarity(idx);
663
664 if (entry.trigger)
665 {
666 entry.trigger = 1;
667 entry.mask = 1; // disable
668 #if 0
669 entry.dest.logical.logical_dest = OnlineCPUs;
670 #else
671 entry.dest.logical.logical_dest = 1 << 0;
672 #endif
673 }
674
675 irq = Pin2Irq(idx, apic, pin);
676 AddPinToIrq(irq, apic, pin);
677
678 vector = AssignIrqVector(irq);
679 entry.vector = vector;
680
681 DPRINT("vector 0x%.08x assigned to irq 0x%.02x\n", vector, irq);
682
683 if (irq == 0)
684 {
685 /* Mask timer IRQ */
686 entry.mask = 1;
687 }
688
689 if ((apic == 0) && (irq < 16))
690 {
691 Disable8259AIrq(irq);
692 }
693 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1));
694 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0));
695
696 IrqPinMap[irq] = pin;
697 IrqApicMap[irq] = apic;
698
699 DPRINT("Vector %x, Pin %x, Irq %x\n", vector, pin, irq);
700 }
701 }
702 }
703
704
705 static VOID IOAPICEnable(VOID)
706 {
707 ULONG i, tmp;
708
709 for (i = 0; i < PIN_MAP_SIZE; i++)
710 {
711 irq_2_pin[i].pin = -1;
712 irq_2_pin[i].next = 0;
713 }
714
715 /*
716 * The number of IO-APIC IRQ registers (== #pins):
717 */
718 for (i = 0; i < IOAPICCount; i++)
719 {
720 tmp = IOAPICRead(i, IOAPIC_VER);
721 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1;
722 }
723
724 /*
725 * Do not trust the IO-APIC being empty at bootup
726 */
727 IOAPICClearAll();
728 }
729
730 #if 0
731 static VOID IOAPICDisable(VOID)
732 {
733 /*
734 * Clear the IO-APIC before rebooting
735 */
736 IOAPICClearAll();
737 APICDisable();
738 }
739 #endif
740
741
742 static VOID IOAPICSetup(VOID)
743 {
744 IOAPICEnable();
745 IOAPICSetupIds();
746 APICSyncArbIDs();
747 IOAPICSetupIrqs();
748 }
749
750
751 VOID IOAPICDump(VOID)
752 {
753 ULONG apic, i;
754 ULONG reg0, reg1, reg2=0;
755
756 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount);
757 for (i = 0; i < IOAPICCount; i++)
758 {
759 DbgPrint("Number of IO-APIC #%d registers: %d.\n",
760 IOAPICMap[i].ApicId,
761 IOAPICMap[i].EntryCount);
762 }
763
764 /*
765 * We are a bit conservative about what we expect. We have to
766 * know about every hardware change ASAP.
767 */
768 DbgPrint("Testing the IO APIC.......................\n");
769
770 for (apic = 0; apic < IOAPICCount; apic++)
771 {
772 reg0 = IOAPICRead(apic, IOAPIC_ID);
773 reg1 = IOAPICRead(apic, IOAPIC_VER);
774 if (GET_IOAPIC_VERSION(reg1) >= 0x10)
775 {
776 reg2 = IOAPICRead(apic, IOAPIC_ARB);
777 }
778
779 DbgPrint("\n");
780 DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId);
781 DbgPrint(".... register #00: %08X\n", reg0);
782 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0));
783 if (reg0 & 0xF0FFFFFF)
784 {
785 DbgPrint(" WARNING: Unexpected IO-APIC\n");
786 }
787
788 DbgPrint(".... register #01: %08X\n", reg1);
789 i = GET_IOAPIC_MRE(reg1);
790
791 DbgPrint("....... : max redirection entries: %04X\n", i);
792 if ((i != 0x0f) && /* older (Neptune) boards */
793 (i != 0x17) && /* typical ISA+PCI boards */
794 (i != 0x1b) && /* Compaq Proliant boards */
795 (i != 0x1f) && /* dual Xeon boards */
796 (i != 0x22) && /* bigger Xeon boards */
797 (i != 0x2E) &&
798 (i != 0x3F))
799 {
800 DbgPrint(" WARNING: Unexpected IO-APIC\n");
801 }
802
803 i = GET_IOAPIC_VERSION(reg1);
804 DbgPrint("....... : IO APIC version: %04X\n", i);
805 if ((i != 0x01) && /* 82489DX IO-APICs */
806 (i != 0x10) && /* oldest IO-APICs */
807 (i != 0x11) && /* Pentium/Pro IO-APICs */
808 (i != 0x13)) /* Xeon IO-APICs */
809 {
810 DbgPrint(" WARNING: Unexpected IO-APIC\n");
811 }
812
813 if (reg1 & 0xFF00FF00)
814 {
815 DbgPrint(" WARNING: Unexpected IO-APIC\n");
816 }
817
818 if (GET_IOAPIC_VERSION(reg1) >= 0x10)
819 {
820 DbgPrint(".... register #02: %08X\n", reg2);
821 DbgPrint("....... : arbitration: %02X\n",
822 GET_IOAPIC_ARB(reg2));
823 if (reg2 & 0xF0FFFFFF)
824 {
825 DbgPrint(" WARNING: Unexpected IO-APIC\n");
826 }
827 }
828
829 DbgPrint(".... IRQ redirection table:\n");
830 DbgPrint(" NR Log Phy Mask Trig IRR Pol"
831 " Stat Dest Deli Vect: \n");
832
833 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++)
834 {
835 IOAPIC_ROUTE_ENTRY entry;
836
837 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2);
838 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2);
839
840 DbgPrint(" %02x %03X %02X ",
841 i,
842 entry.dest.logical.logical_dest,
843 entry.dest.physical.physical_dest);
844
845 DbgPrint("%C %C %1d %C %C %C %03X %02X\n",
846 (entry.mask == 0) ? 'U' : 'M', // Unmasked/masked
847 (entry.trigger == 0) ? 'E' : 'L', // Edge/level sensitive
848 entry.irr,
849 (entry.polarity == 0) ? 'H' : 'L', // Active high/active low
850 (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending
851 (entry.dest_mode == 0) ? 'P' : 'L', // Physical logical
852 entry.delivery_mode,
853 entry.vector);
854 }
855 }
856 DbgPrint("IRQ to pin mappings:\n");
857 for (i = 0; i < PIC_IRQS; i++)
858 {
859 struct irq_pin_list *entry = irq_2_pin + i;
860 if (entry->pin < 0)
861 {
862 continue;
863 }
864 DbgPrint("IRQ%d ", i);
865 for (;;)
866 {
867 DbgPrint("-> %d", entry->pin);
868 if (!entry->next)
869 {
870 break;
871 }
872 entry = irq_2_pin + entry->next;
873 }
874 if (i % 2)
875 {
876 DbgPrint("\n");
877 }
878 else
879 {
880 DbgPrint(" ");
881 }
882 }
883
884 DbgPrint(".................................... done.\n");
885 }
886
887
888
889 /* Functions for handling local APICs */
890
891 ULONG Read8254Timer(VOID)
892 {
893 ULONG Count;
894
895 WRITE_PORT_UCHAR((PUCHAR)0x43, 0x00);
896 Count = READ_PORT_UCHAR((PUCHAR)0x40);
897 Count |= READ_PORT_UCHAR((PUCHAR)0x40) << 8;
898
899 return Count;
900 }
901
902 VOID WaitFor8254Wraparound(VOID)
903 {
904 ULONG CurCount, PrevCount = ~0;
905 LONG Delta;
906
907 CurCount = Read8254Timer();
908 do
909 {
910 PrevCount = CurCount;
911 CurCount = Read8254Timer();
912 Delta = CurCount - PrevCount;
913
914 /*
915 * This limit for delta seems arbitrary, but it isn't, it's
916 * slightly above the level of error a buggy Mercury/Neptune
917 * chipset timer can cause.
918 */
919
920 }
921 while (Delta < 300);
922 }
923
924 #define HZ (100)
925 #define APIC_DIVISOR (16)
926
927 VOID APICSetupLVTT(ULONG ClockTicks)
928 {
929 ULONG tmp;
930
931 tmp = GET_APIC_VERSION(APICRead(APIC_VER));
932 if (!APIC_INTEGRATED(tmp))
933 {
934 tmp = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;;
935 }
936 else
937 {
938 /* Periodic timer */
939 tmp = APIC_LVT_PERIODIC | LOCAL_TIMER_VECTOR;;
940 }
941 APICWrite(APIC_LVTT, tmp);
942
943 tmp = APICRead(APIC_TDCR);
944 tmp &= ~(APIC_TDCR_1 | APIC_TIMER_BASE_DIV);
945 tmp |= APIC_TDCR_16;
946 APICWrite(APIC_TDCR, tmp);
947 APICWrite(APIC_ICRT, ClockTicks / APIC_DIVISOR);
948 }
949
950
951 VOID APICCalibrateTimer(ULONG CPU)
952 {
953 ULARGE_INTEGER t1, t2;
954 LONG tt1, tt2;
955
956 DPRINT("Calibrating APIC timer for CPU %d\n", CPU);
957
958 APICSetupLVTT(1000000000);
959
960 /*
961 * The timer chip counts down to zero. Let's wait
962 * for a wraparound to start exact measurement:
963 * (the current tick might have been already half done)
964 */
965 WaitFor8254Wraparound();
966
967 /*
968 * We wrapped around just now. Let's start
969 */
970 ReadPentiumClock(&t1);
971 tt1 = APICRead(APIC_CCRT);
972
973 WaitFor8254Wraparound();
974
975
976 tt2 = APICRead(APIC_CCRT);
977 ReadPentiumClock(&t2);
978
979 CPUMap[CPU].BusSpeed = (HZ * (long)(tt1 - tt2) * APIC_DIVISOR);
980 CPUMap[CPU].CoreSpeed = (HZ * (t2.QuadPart - t1.QuadPart));
981
982 /* Setup timer for normal operation */
983 // APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100); // 100ns
984 APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 10000); // 10ms
985 // APICSetupLVTT((CPUMap[CPU].BusSpeed / 1000000) * 100000); // 100ms
986
987 DPRINT("CPU clock speed is %ld.%04ld MHz.\n",
988 CPUMap[CPU].CoreSpeed/1000000,
989 CPUMap[CPU].CoreSpeed%1000000);
990
991 DPRINT("Host bus clock speed is %ld.%04ld MHz.\n",
992 CPUMap[CPU].BusSpeed/1000000,
993 CPUMap[CPU].BusSpeed%1000000);
994 }
995
996 VOID
997 SetInterruptGate(ULONG index, ULONG address)
998 {
999 IDT_DESCRIPTOR *idt;
1000
1001 idt = (IDT_DESCRIPTOR*)((ULONG)KeGetCurrentKPCR()->IDT + index * sizeof(IDT_DESCRIPTOR));
1002 idt->a = (((ULONG)address)&0xffff) + (KERNEL_CS << 16);
1003 idt->b = 0x8e00 + (((ULONG)address)&0xffff0000);
1004 }
1005
1006 #endif /* MP */
1007
1008 VOID STDCALL
1009 HalInitializeProcessor(ULONG ProcessorNumber,
1010 PVOID /*PLOADER_PARAMETER_BLOCK*/ LoaderBlock)
1011 {
1012 #ifdef MP
1013 ULONG CPU;
1014 #endif
1015
1016 DPRINT("HalInitializeProcessor(%x %x)\n", ProcessorNumber, LoaderBlock);
1017
1018 #ifdef MP
1019 CPU = ThisCPU();
1020 if (OnlineCPUs & (1 << CPU))
1021 {
1022 KEBUGCHECK(0);
1023 }
1024
1025 if (ProcessorNumber == 0)
1026 {
1027 HaliInitBSP();
1028 }
1029 else
1030 {
1031 APICSetup();
1032
1033 DPRINT("CPU %d says it is now booted.\n", CPU);
1034
1035 APICCalibrateTimer(CPU);
1036 }
1037
1038 /* This processor is now booted */
1039 CPUMap[CPU].Flags |= CPU_ENABLED;
1040 OnlineCPUs |= (1 << CPU);
1041
1042 /* Setup busy waiting */
1043 HalpCalibrateStallExecution();
1044 #endif
1045 }
1046
1047 BOOLEAN STDCALL
1048 HalAllProcessorsStarted (VOID)
1049 {
1050 DPRINT("HalAllProcessorsStarted()\n");
1051
1052 #ifdef MP
1053
1054 ULONG CPUs = 0, i;
1055 for (i = 0; i < 32; i++)
1056 {
1057 if (OnlineCPUs & (1 << i))
1058 {
1059 CPUs++;
1060 }
1061 }
1062 if (CPUs > CPUCount)
1063 {
1064 KEBUGCHECK(0);
1065 }
1066 else if (CPUs == CPUCount)
1067 {
1068 IOAPICSetup();
1069 return TRUE;
1070 }
1071 return FALSE;
1072
1073 #else /* MP */
1074
1075 return TRUE;
1076
1077 #endif /* MP */
1078 }
1079
1080 BOOLEAN STDCALL
1081 HalStartNextProcessor(ULONG Unknown1,
1082 ULONG ProcessorStack)
1083 {
1084 #ifdef MP
1085 PCOMMON_AREA_INFO Common;
1086 ULONG StartupCount;
1087 ULONG DeliveryStatus = 0;
1088 ULONG AcceptStatus = 0;
1089 ULONG CPU, i, j;
1090 ULONG tmp, maxlvt;
1091
1092 DPRINT("HalStartNextProcessor(%x %x)\n", Unknown1, ProcessorStack);
1093
1094 for (CPU = 0; CPU < CPUCount; CPU++)
1095 {
1096 if (!(OnlineCPUs & (1<<CPU)))
1097 {
1098 break;
1099 }
1100 }
1101
1102 if (CPU >= CPUCount)
1103 {
1104 KEBUGCHECK(0);
1105 }
1106
1107 DPRINT1("Attempting to boot CPU %d\n", CPU);
1108
1109 /* Send INIT IPI */
1110
1111 APICSendIPI(CPU, APIC_DM_INIT|APIC_ICR0_LEVEL_ASSERT);
1112
1113 KeStallExecutionProcessor(200);
1114
1115 /* Deassert INIT */
1116
1117 APICSendIPI(CPU, APIC_DM_INIT|APIC_ICR0_LEVEL_DEASSERT);
1118
1119 if (APIC_INTEGRATED(CPUMap[CPU].APICVersion))
1120 {
1121 /* Clear APIC errors */
1122 APICWrite(APIC_ESR, 0);
1123 tmp = (APICRead(APIC_ESR) & APIC_ESR_MASK);
1124 }
1125
1126 Common = (PCOMMON_AREA_INFO)CommonBase;
1127
1128 /* Write the location of the AP stack */
1129 Common->Stack = (ULONG)ProcessorStack;
1130
1131 DPRINT("CPU %d got stack at 0x%X\n", CPU, Common->Stack);
1132 #if 0
1133 for (j = 0; j < 16; j++)
1134 {
1135 Common->Debug[j] = 0;
1136 }
1137 #endif
1138
1139 maxlvt = APICGetMaxLVT();
1140
1141 /* Is this a local APIC or an 82489DX? */
1142 StartupCount = (APIC_INTEGRATED(CPUMap[CPU].APICVersion)) ? 2 : 0;
1143
1144 for (i = 1; i <= StartupCount; i++)
1145 {
1146 /* It's a local APIC, so send STARTUP IPI */
1147 DPRINT("Sending startup signal %d\n", i);
1148 /* Clear errors */
1149 APICWrite(APIC_ESR, 0);
1150 APICRead(APIC_ESR);
1151
1152 APICSendIPI(CPU, APIC_DM_STARTUP | ((COMMON_AREA + PAGE_SIZE) >> 12)|APIC_ICR0_LEVEL_DEASSERT);
1153
1154 /* Wait up to 10ms for IPI to be delivered */
1155 j = 0;
1156 do
1157 {
1158 KeStallExecutionProcessor(10);
1159
1160 /* Check Delivery Status */
1161 DeliveryStatus = APICRead(APIC_ICR0) & APIC_ICR0_DS;
1162
1163 j++;
1164 } while ((DeliveryStatus) && (j < 1000));
1165
1166 KeStallExecutionProcessor(200);
1167
1168 /*
1169 * Due to the Pentium erratum 3AP.
1170 */
1171 if (maxlvt > 3)
1172 {
1173 APICRead(APIC_SIVR);
1174 APICWrite(APIC_ESR, 0);
1175 }
1176
1177 AcceptStatus = APICRead(APIC_ESR) & APIC_ESR_MASK;
1178
1179 if (DeliveryStatus || AcceptStatus)
1180 {
1181 break;
1182 }
1183 }
1184
1185 if (DeliveryStatus)
1186 {
1187 DPRINT("STARTUP IPI for CPU %d was never delivered.\n", CPU);
1188 }
1189
1190 if (AcceptStatus)
1191 {
1192 DPRINT("STARTUP IPI for CPU %d was never accepted.\n", CPU);
1193 }
1194
1195 if (!(DeliveryStatus || AcceptStatus))
1196 {
1197
1198 /* Wait no more than 5 seconds for processor to boot */
1199 DPRINT("Waiting for 5 seconds for CPU %d to boot\n", CPU);
1200
1201 /* Wait no more than 5 seconds */
1202 for (j = 0; j < 50000; j++)
1203 {
1204 if (CPUMap[CPU].Flags & CPU_ENABLED)
1205 {
1206 break;
1207 }
1208 KeStallExecutionProcessor(100);
1209 }
1210 }
1211
1212 if (CPUMap[CPU].Flags & CPU_ENABLED)
1213 {
1214 DbgPrint("CPU %d is now running\n", CPU);
1215 }
1216 else
1217 {
1218 DbgPrint("Initialization of CPU %d failed\n", CPU);
1219 }
1220
1221 #if 0
1222 DPRINT("Debug bytes are:\n");
1223
1224 for (j = 0; j < 4; j++)
1225 {
1226 DPRINT("0x%08X 0x%08X 0x%08X 0x%08X.\n",
1227 Common->Debug[j*4+0],
1228 Common->Debug[j*4+1],
1229 Common->Debug[j*4+2],
1230 Common->Debug[j*4+3]);
1231 }
1232 #endif
1233
1234 #endif /* MP */
1235 return TRUE;
1236 }
1237
1238
1239 #ifdef MP
1240
1241 ULONG MPChecksum(PUCHAR Base,
1242 ULONG Size)
1243 /*
1244 * Checksum an MP configuration block
1245 */
1246 {
1247 ULONG Sum = 0;
1248
1249 while (Size--)
1250 Sum += *Base++;
1251
1252 return((UCHAR)Sum);
1253 }
1254
1255
1256 PCHAR HaliMPFamily(ULONG Family,
1257 ULONG Model)
1258 {
1259 static CHAR str[32];
1260 static PCHAR CPUs[] =
1261 {
1262 "80486DX", "80486DX",
1263 "80486SX", "80486DX/2 or 80487",
1264 "80486SL", "Intel5X2(tm)",
1265 "Unknown", "Unknown",
1266 "80486DX/4"
1267 };
1268 if (Family == 0x6)
1269 return ("Pentium(tm) Pro");
1270 if (Family == 0x5)
1271 return ("Pentium(tm)");
1272 if (Family == 0x0F && Model == 0x0F)
1273 return("Special controller");
1274 if (Family == 0x0F && Model == 0x00)
1275 return("Pentium 4(tm)");
1276 if (Family == 0x04 && Model < 9)
1277 return CPUs[Model];
1278 sprintf(str, "Unknown CPU with family ID %ld and model ID %ld", Family, Model);
1279 return str;
1280 }
1281
1282 static VOID HaliMPProcessorInfo(PMP_CONFIGURATION_PROCESSOR m)
1283 {
1284 ULONG ver;
1285
1286 if (!(m->CpuFlags & CPU_FLAG_ENABLED))
1287 return;
1288
1289 DPRINT("Processor #%d %s APIC version %d\n",
1290 m->ApicId,
1291 HaliMPFamily((m->FeatureFlags & CPU_FAMILY_MASK) >> 8,
1292 (m->FeatureFlags & CPU_MODEL_MASK) >> 4),
1293 m->ApicVersion);
1294
1295 if (m->FeatureFlags & (1 << 0))
1296 DPRINT(" Floating point unit present.\n");
1297 if (m->FeatureFlags & (1 << 7))
1298 DPRINT(" Machine Exception supported.\n");
1299 if (m->FeatureFlags & (1 << 8))
1300 DPRINT(" 64 bit compare & exchange supported.\n");
1301 if (m->FeatureFlags & (1 << 9))
1302 DPRINT(" Internal APIC present.\n");
1303 if (m->FeatureFlags & (1 << 11))
1304 DPRINT(" SEP present.\n");
1305 if (m->FeatureFlags & (1 << 12))
1306 DPRINT(" MTRR present.\n");
1307 if (m->FeatureFlags & (1 << 13))
1308 DPRINT(" PGE present.\n");
1309 if (m->FeatureFlags & (1 << 14))
1310 DPRINT(" MCA present.\n");
1311 if (m->FeatureFlags & (1 << 15))
1312 DPRINT(" CMOV present.\n");
1313 if (m->FeatureFlags & (1 << 16))
1314 DPRINT(" PAT present.\n");
1315 if (m->FeatureFlags & (1 << 17))
1316 DPRINT(" PSE present.\n");
1317 if (m->FeatureFlags & (1 << 18))
1318 DPRINT(" PSN present.\n");
1319 if (m->FeatureFlags & (1 << 19))
1320 DPRINT(" Cache Line Flush Instruction present.\n");
1321 /* 20 Reserved */
1322 if (m->FeatureFlags & (1 << 21))
1323 DPRINT(" Debug Trace and EMON Store present.\n");
1324 if (m->FeatureFlags & (1 << 22))
1325 DPRINT(" ACPI Thermal Throttle Registers present.\n");
1326 if (m->FeatureFlags & (1 << 23))
1327 DPRINT(" MMX present.\n");
1328 if (m->FeatureFlags & (1 << 24))
1329 DPRINT(" FXSR present.\n");
1330 if (m->FeatureFlags & (1 << 25))
1331 DPRINT(" XMM present.\n");
1332 if (m->FeatureFlags & (1 << 26))
1333 DPRINT(" Willamette New Instructions present.\n");
1334 if (m->FeatureFlags & (1 << 27))
1335 DPRINT(" Self Snoop present.\n");
1336 /* 28 Reserved */
1337 if (m->FeatureFlags & (1 << 29))
1338 DPRINT(" Thermal Monitor present.\n");
1339 /* 30, 31 Reserved */
1340
1341 CPUMap[CPUCount].APICId = m->ApicId;
1342
1343 CPUMap[CPUCount].Flags = CPU_USABLE;
1344
1345 if (m->CpuFlags & CPU_FLAG_BSP)
1346 {
1347 DPRINT(" Bootup CPU\n");
1348 CPUMap[CPUCount].Flags |= CPU_BSP;
1349 BootCPU = m->ApicId;
1350 }
1351
1352 if (m->ApicId > MAX_CPU)
1353 {
1354 DPRINT("Processor #%d INVALID. (Max ID: %d).\n", m->ApicId, MAX_CPU);
1355 return;
1356 }
1357 ver = m->ApicVersion;
1358
1359 /*
1360 * Validate version
1361 */
1362 if (ver == 0x0)
1363 {
1364 DPRINT("BIOS bug, APIC version is 0 for CPU#%d! fixing up to 0x10. (tell your hw vendor)\n", m->ApicId);
1365 ver = 0x10;
1366 }
1367 CPUMap[CPUCount].APICVersion = ver;
1368
1369 CPUCount++;
1370 }
1371
1372 static VOID HaliMPBusInfo(PMP_CONFIGURATION_BUS m)
1373 {
1374 static ULONG CurrentPCIBusId = 0;
1375 CHAR str[7];
1376
1377 memcpy(str, m->BusType, 6);
1378 str[6] = 0;
1379 DPRINT("Bus #%d is %s\n", m->BusId, str);
1380
1381 if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0)
1382 {
1383 BUSMap[m->BusId] = MP_BUS_ISA;
1384 }
1385 else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0)
1386 {
1387 BUSMap[m->BusId] = MP_BUS_EISA;
1388 }
1389 else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0)
1390 {
1391 BUSMap[m->BusId] = MP_BUS_PCI;
1392 PCIBUSMap[m->BusId] = CurrentPCIBusId;
1393 CurrentPCIBusId++;
1394 }
1395 else if (strncmp(str, BUSTYPE_MCA, sizeof(BUSTYPE_MCA)-1) == 0)
1396 {
1397 BUSMap[m->BusId] = MP_BUS_MCA;
1398 }
1399 else
1400 {
1401 DPRINT("Unknown bustype %s - ignoring\n", str);
1402 }
1403 }
1404
1405 static VOID HaliMPIOApicInfo(PMP_CONFIGURATION_IOAPIC m)
1406 {
1407 if (!(m->ApicFlags & CPU_FLAG_ENABLED))
1408 return;
1409
1410 DPRINT("I/O APIC #%d Version %d at 0x%lX.\n",
1411 m->ApicId, m->ApicVersion, m->ApicAddress);
1412 if (IOAPICCount > MAX_IOAPIC)
1413 {
1414 DPRINT("Max # of I/O APICs (%d) exceeded (found %d).\n",
1415 MAX_IOAPIC, IOAPICCount);
1416 DPRINT1("Recompile with bigger MAX_IOAPIC!.\n");
1417 KEBUGCHECK(0);
1418 }
1419 IOAPICMap[IOAPICCount].ApicId = m->ApicId;
1420 IOAPICMap[IOAPICCount].ApicVersion = m->ApicVersion;
1421 IOAPICMap[IOAPICCount].ApicAddress = m->ApicAddress;
1422 IOAPICCount++;
1423 }
1424
1425 static VOID HaliMPIntSrcInfo(PMP_CONFIGURATION_INTSRC m)
1426 {
1427 DPRINT("Int: type %d, pol %d, trig %d, bus %d,"
1428 " IRQ %02x, APIC ID %x, APIC INT %02x\n",
1429 m->IrqType, m->IrqFlag & 3,
1430 (m->IrqFlag >> 2) & 3, m->SrcBusId,
1431 m->SrcBusIrq, m->DstApicId, m->DstApicInt);
1432 if (IRQCount > MAX_IRQ_SOURCE)
1433 {
1434 DPRINT1("Max # of irq sources exceeded!!\n");
1435 KEBUGCHECK(0);
1436 }
1437
1438 IRQMap[IRQCount] = *m;
1439 IRQCount++;
1440 }
1441
1442 static VOID HaliMPIntLocalInfo(PMP_CONFIGURATION_INTLOCAL m)
1443 {
1444 DPRINT("Lint: type %d, pol %d, trig %d, bus %d,"
1445 " IRQ %02x, APIC ID %x, APIC LINT %02x\n",
1446 m->IrqType, m->IrqFlag & 3,
1447 (m->IrqFlag >> 2) & 3, m->SrcBusId,
1448 m->SrcBusIrq, m->DstApicId, m->DstApicLInt);
1449 /*
1450 * Well it seems all SMP boards in existence
1451 * use ExtINT/LVT1 == LINT0 and
1452 * NMI/LVT2 == LINT1 - the following check
1453 * will show us if this assumptions is false.
1454 * Until then we do not have to add baggage.
1455 */
1456 if ((m->IrqType == INT_EXTINT) && (m->DstApicLInt != 0))
1457 {
1458 DPRINT1("Invalid MP table!\n");
1459 KEBUGCHECK(0);
1460 }
1461 if ((m->IrqType == INT_NMI) && (m->DstApicLInt != 1))
1462 {
1463 DPRINT1("Invalid MP table!\n");
1464 KEBUGCHECK(0);
1465 }
1466 }
1467
1468 VOID
1469 HaliReadMPConfigTable(PMP_CONFIGURATION_TABLE Table)
1470 /*
1471 PARAMETERS:
1472 Table = Pointer to MP configuration table
1473 */
1474 {
1475 PUCHAR Entry;
1476 ULONG Count;
1477
1478 if (Table->Signature != MPC_SIGNATURE)
1479 {
1480 PUCHAR pc = (PUCHAR)&Table->Signature;
1481
1482 DbgPrint("Bad MP configuration block signature: %c%c%c%c\n",
1483 pc[0], pc[1], pc[2], pc[3]);
1484 KEBUGCHECK(0);
1485 return;
1486 }
1487
1488 if (MPChecksum((PUCHAR)Table, Table->Length))
1489 {
1490 DbgPrint("Bad MP configuration block checksum\n");
1491 KEBUGCHECK(0);
1492 return;
1493 }
1494
1495 if (Table->Specification < 0x04)
1496 {
1497 DbgPrint("Bad MP configuration table version (%d)\n",
1498 Table->Specification);
1499 KEBUGCHECK(0);
1500 return;
1501 }
1502
1503 if (Table->LocalAPICAddress != APIC_DEFAULT_BASE)
1504 {
1505 DbgPrint("APIC base address is at 0x%X. " \
1506 "I cannot handle non-standard adresses\n", Table->LocalAPICAddress);
1507 KEBUGCHECK(0);
1508 }
1509
1510 Entry = (PUCHAR)((PVOID)Table + sizeof(MP_CONFIGURATION_TABLE));
1511 Count = 0;
1512 while (Count < (Table->Length - sizeof(MP_CONFIGURATION_TABLE)))
1513 {
1514 /* Switch on type */
1515 switch (*Entry)
1516 {
1517 case MPCTE_PROCESSOR:
1518 {
1519 HaliMPProcessorInfo((PMP_CONFIGURATION_PROCESSOR)Entry);
1520 Entry += sizeof(MP_CONFIGURATION_PROCESSOR);
1521 Count += sizeof(MP_CONFIGURATION_PROCESSOR);
1522 break;
1523 }
1524 case MPCTE_BUS:
1525 {
1526 HaliMPBusInfo((PMP_CONFIGURATION_BUS)Entry);
1527 Entry += sizeof(MP_CONFIGURATION_BUS);
1528 Count += sizeof(MP_CONFIGURATION_BUS);
1529 break;
1530 }
1531 case MPCTE_IOAPIC:
1532 {
1533 HaliMPIOApicInfo((PMP_CONFIGURATION_IOAPIC)Entry);
1534 Entry += sizeof(MP_CONFIGURATION_IOAPIC);
1535 Count += sizeof(MP_CONFIGURATION_IOAPIC);
1536 break;
1537 }
1538 case MPCTE_INTSRC:
1539 {
1540 HaliMPIntSrcInfo((PMP_CONFIGURATION_INTSRC)Entry);
1541 Entry += sizeof(MP_CONFIGURATION_INTSRC);
1542 Count += sizeof(MP_CONFIGURATION_INTSRC);
1543 break;
1544 }
1545 case MPCTE_LINTSRC:
1546 {
1547 HaliMPIntLocalInfo((PMP_CONFIGURATION_INTLOCAL)Entry);
1548 Entry += sizeof(MP_CONFIGURATION_INTLOCAL);
1549 Count += sizeof(MP_CONFIGURATION_INTLOCAL);
1550 break;
1551 }
1552 default:
1553 DbgPrint("Unknown entry in MPC table\n");
1554 KEBUGCHECK(0);
1555 }
1556 }
1557 }
1558
1559 static VOID HaliConstructDefaultIOIrqMPTable(ULONG Type)
1560 {
1561 MP_CONFIGURATION_INTSRC intsrc;
1562 ULONG i;
1563
1564 intsrc.Type = MPCTE_INTSRC;
1565 intsrc.IrqFlag = 0; /* conforming */
1566 intsrc.SrcBusId = 0;
1567 intsrc.DstApicId = IOAPICMap[0].ApicId;
1568
1569 intsrc.IrqType = INT_VECTORED;
1570 for (i = 0; i < 16; i++) {
1571 switch (Type) {
1572 case 2:
1573 if (i == 0 || i == 13)
1574 continue; /* IRQ0 & IRQ13 not connected */
1575 /* Fall through */
1576 default:
1577 if (i == 2)
1578 continue; /* IRQ2 is never connected */
1579 }
1580
1581 intsrc.SrcBusIrq = i;
1582 intsrc.DstApicInt = i ? i : 2; /* IRQ0 to INTIN2 */
1583 HaliMPIntSrcInfo(&intsrc);
1584 }
1585
1586 intsrc.IrqType = INT_EXTINT;
1587 intsrc.SrcBusIrq = 0;
1588 intsrc.DstApicInt = 0; /* 8259A to INTIN0 */
1589 HaliMPIntSrcInfo(&intsrc);
1590 }
1591
1592 static VOID HaliConstructDefaultISAMPTable(ULONG Type)
1593 {
1594 MP_CONFIGURATION_PROCESSOR processor;
1595 MP_CONFIGURATION_BUS bus;
1596 MP_CONFIGURATION_IOAPIC ioapic;
1597 MP_CONFIGURATION_INTLOCAL lintsrc;
1598 ULONG linttypes[2] = { INT_EXTINT, INT_NMI };
1599 ULONG i;
1600
1601 /*
1602 * 2 CPUs, numbered 0 & 1.
1603 */
1604 processor.Type = MPCTE_PROCESSOR;
1605 /* Either an integrated APIC or a discrete 82489DX. */
1606 processor.ApicVersion = Type > 4 ? 0x10 : 0x01;
1607 processor.CpuFlags = CPU_FLAG_ENABLED | CPU_FLAG_BSP;
1608 /* FIXME: Get this from the bootstrap processor */
1609 processor.CpuSignature = 0;
1610 processor.FeatureFlags = 0;
1611 processor.Reserved[0] = 0;
1612 processor.Reserved[1] = 0;
1613 for (i = 0; i < 2; i++)
1614 {
1615 processor.ApicId = i;
1616 HaliMPProcessorInfo(&processor);
1617 processor.CpuFlags &= ~CPU_FLAG_BSP;
1618 }
1619
1620 bus.Type = MPCTE_BUS;
1621 bus.BusId = 0;
1622 switch (Type)
1623 {
1624 default:
1625 DPRINT("Unknown standard configuration %d\n", Type);
1626 /* Fall through */
1627 case 1:
1628 case 5:
1629 memcpy(bus.BusType, "ISA ", 6);
1630 break;
1631 case 2:
1632 case 6:
1633 case 3:
1634 memcpy(bus.BusType, "EISA ", 6);
1635 break;
1636 case 4:
1637 case 7:
1638 memcpy(bus.BusType, "MCA ", 6);
1639 }
1640 HaliMPBusInfo(&bus);
1641 if (Type > 4)
1642 {
1643 bus.Type = MPCTE_BUS;
1644 bus.BusId = 1;
1645 memcpy(bus.BusType, "PCI ", 6);
1646 HaliMPBusInfo(&bus);
1647 }
1648
1649 ioapic.Type = MPCTE_IOAPIC;
1650 ioapic.ApicId = 2;
1651 ioapic.ApicVersion = Type > 4 ? 0x10 : 0x01;
1652 ioapic.ApicFlags = MP_IOAPIC_USABLE;
1653 ioapic.ApicAddress = IOAPIC_DEFAULT_BASE;
1654 HaliMPIOApicInfo(&ioapic);
1655
1656 /*
1657 * We set up most of the low 16 IO-APIC pins according to MPS rules.
1658 */
1659 HaliConstructDefaultIOIrqMPTable(Type);
1660
1661 lintsrc.Type = MPCTE_LINTSRC;
1662 lintsrc.IrqType = 0;
1663 lintsrc.IrqFlag = 0; /* conforming */
1664 lintsrc.SrcBusId = 0;
1665 lintsrc.SrcBusIrq = 0;
1666 lintsrc.DstApicId = MP_APIC_ALL;
1667 for (i = 0; i < 2; i++)
1668 {
1669 lintsrc.IrqType = linttypes[i];
1670 lintsrc.DstApicLInt = i;
1671 HaliMPIntLocalInfo(&lintsrc);
1672 }
1673 }
1674
1675
1676 BOOLEAN
1677 HaliScanForMPConfigTable(ULONG Base,
1678 ULONG Size)
1679 /*
1680 PARAMETERS:
1681 Base = Base address of region
1682 Size = Length of region to check
1683 RETURNS:
1684 TRUE if a valid MP configuration table was found
1685 */
1686 {
1687 PULONG bp = (PULONG)Base;
1688 MP_FLOATING_POINTER* mpf;
1689
1690 while (Size > 0)
1691 {
1692 if (*bp == MPF_SIGNATURE)
1693 {
1694 DbgPrint("Found MPF signature at %x, checksum %x\n", bp,
1695 MPChecksum((PUCHAR)bp, 16));
1696 if (MPChecksum((PUCHAR)bp, 16) == 0)
1697 {
1698 mpf = (MP_FLOATING_POINTER*)bp;
1699
1700 DbgPrint("Intel MultiProcessor Specification v1.%d compliant system.\n",
1701 mpf->Specification);
1702
1703 if (mpf->Feature2 & FEATURE2_IMCRP) {
1704 APICMode = amPIC;
1705 DPRINT("Running in IMCR and PIC compatibility mode.\n");
1706 } else {
1707 APICMode = amVWIRE;
1708 DPRINT("Running in Virtual Wire compatibility mode.\n");
1709 }
1710
1711 switch (mpf->Feature1)
1712 {
1713 case 0:
1714 /* Non standard configuration */
1715 break;
1716 case 1:
1717 DPRINT("ISA\n");
1718 break;
1719 case 2:
1720 DPRINT("EISA with no IRQ8 chaining\n");
1721 break;
1722 case 3:
1723 DPRINT("EISA\n");
1724 break;
1725 case 4:
1726 DPRINT("MCA\n");
1727 break;
1728 case 5:
1729 DPRINT("ISA and PCI\n");
1730 break;
1731 case 6:
1732 DPRINT("EISA and PCI\n");
1733 break;
1734 case 7:
1735 DPRINT("MCA and PCI\n");
1736 break;
1737 default:
1738 DbgPrint("Unknown standard configuration %d\n", mpf->Feature1);
1739 return FALSE;
1740 }
1741
1742 CPUCount = 0;
1743 IOAPICCount = 0;
1744 IRQCount = 0;
1745
1746 if ((mpf->Feature1 == 0) && (mpf->Address)) {
1747 HaliReadMPConfigTable((PMP_CONFIGURATION_TABLE)mpf->Address);
1748 } else {
1749 HaliConstructDefaultISAMPTable(mpf->Feature1);
1750 }
1751
1752 return TRUE;
1753 }
1754 }
1755 bp += 4;
1756 Size -= 16;
1757 }
1758 return FALSE;
1759 }
1760
1761 VOID
1762 HalpInitMPS(VOID)
1763 {
1764 USHORT EBDA;
1765 static BOOLEAN MPSInitialized = FALSE;
1766
1767
1768 /* Only initialize MP system once. Once called the first time,
1769 each subsequent call is part of the initialization sequence
1770 for an application processor. */
1771
1772 DPRINT("HalpInitMPS()\n");
1773
1774
1775 if (MPSInitialized)
1776 {
1777 KEBUGCHECK(0);
1778 }
1779
1780 MPSInitialized = TRUE;
1781
1782 /*
1783 Scan the system memory for an MP configuration table
1784 1) Scan the first KB of system base memory
1785 2) Scan the last KB of system base memory
1786 3) Scan the BIOS ROM address space between 0F0000h and 0FFFFFh
1787 4) Scan the Extended BIOS Data Area
1788 */
1789
1790 if (!HaliScanForMPConfigTable(0x0, 0x400))
1791 {
1792 if (!HaliScanForMPConfigTable(0x9FC00, 0x400))
1793 {
1794 if (!HaliScanForMPConfigTable(0xF0000, 0x10000))
1795 {
1796 EBDA = *((PUSHORT)0x040E);
1797 EBDA <<= 4;
1798 if (!HaliScanForMPConfigTable((ULONG)EBDA, 0x1000))
1799 {
1800 DbgPrint("No multiprocessor compliant system found.\n");
1801 KEBUGCHECK(0);
1802 }
1803 }
1804 }
1805 }
1806
1807 /* Setup IRQ to vector translation map */
1808 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap));
1809
1810 /* Setup interrupt handlers */
1811 SetInterruptGate(LOCAL_TIMER_VECTOR, (ULONG)MpsTimerInterrupt);
1812 SetInterruptGate(ERROR_VECTOR, (ULONG)MpsErrorInterrupt);
1813 SetInterruptGate(SPURIOUS_VECTOR, (ULONG)MpsSpuriousInterrupt);
1814 SetInterruptGate(IPI_VECTOR, (ULONG)MpsIpiInterrupt);
1815
1816 }
1817
1818 VOID
1819 HaliReconfigurePciInterrupts(VOID)
1820 {
1821 ULONG i;
1822
1823 for (i = 0; i < IRQCount; i++)
1824 {
1825 if (BUSMap[IRQMap[i].SrcBusId] == MP_BUS_PCI)
1826 {
1827 DPRINT("%02x: IrqType %02x, IrqFlag %02x, SrcBusId %02x, SrcBusIrq %02x, DstApicId %02x, DstApicInt %02x\n",
1828 i, IRQMap[i].IrqType, IRQMap[i].IrqFlag, IRQMap[i].SrcBusId,
1829 IRQMap[i].SrcBusIrq, IRQMap[i].DstApicId, IRQMap[i].DstApicInt);
1830
1831 if(1 != HalSetBusDataByOffset(PCIConfiguration, IRQMap[i].SrcBusId, (IRQMap[i].SrcBusIrq >> 2) & 0x1f, &IRQMap[i].DstApicInt, 0x3c /*PCI_INTERRUPT_LINE*/, 1))
1832 {
1833 CHECKPOINT;
1834 }
1835 }
1836 }
1837
1838 }
1839
1840 #endif /* MP */
1841
1842 /* EOF */