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