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