implemented some stubs needed by ClamWin
[reactos.git] / reactos / hal / halx86 / mp / ioapic.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: hal/halx86/generic/ioapic.c
6 * PURPOSE:
7 * PROGRAMMER:
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *****************************************************************/
17
18 MP_CONFIGURATION_INTSRC IRQMap[MAX_IRQ_SOURCE]; /* Map of all IRQs */
19 ULONG IRQCount = 0; /* Number of IRQs */
20 ULONG IrqApicMap[MAX_IRQ_SOURCE];
21
22 UCHAR BUSMap[MAX_BUS]; /* Map of all buses in the system */
23 UCHAR PCIBUSMap[MAX_BUS]; /* Map of all PCI buses in the system */
24
25 IOAPIC_INFO IOAPICMap[MAX_IOAPIC]; /* Map of all I/O APICs in the system */
26 ULONG IOAPICCount; /* Number of I/O APICs in the system */
27
28 ULONG IRQVectorMap[MAX_IRQ_SOURCE]; /* IRQ to vector map */
29
30 /* EISA interrupts are always polarity zero and can be edge or level
31 * trigger depending on the ELCR value. If an interrupt is listed as
32 * EISA conforming in the MP table, that means its trigger type must
33 * be read in from the ELCR */
34
35 #define default_EISA_trigger(idx) (EISA_ELCR(IRQMap[idx].SrcBusIrq))
36 #define default_EISA_polarity(idx) (0)
37
38 /* ISA interrupts are always polarity zero edge triggered,
39 * when listed as conforming in the MP table. */
40
41 #define default_ISA_trigger(idx) (0)
42 #define default_ISA_polarity(idx) (0)
43
44 /* PCI interrupts are always polarity one level triggered,
45 * when listed as conforming in the MP table. */
46
47 #define default_PCI_trigger(idx) (1)
48 #define default_PCI_polarity(idx) (1)
49
50 /* MCA interrupts are always polarity zero level triggered,
51 * when listed as conforming in the MP table. */
52
53 #define default_MCA_trigger(idx) (1)
54 #define default_MCA_polarity(idx) (0)
55
56 /***************************************************************************/
57
58 extern VOID Disable8259AIrq(ULONG irq);
59 ULONG IOAPICRead(ULONG Apic, ULONG Offset);
60 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value);
61
62 /* FUNCTIONS ***************************************************************/
63
64 /*
65 * EISA Edge/Level control register, ELCR
66 */
67 static ULONG EISA_ELCR(ULONG irq)
68 {
69 if (irq < 16)
70 {
71 PUCHAR port = (PUCHAR)(0x4d0 + (irq >> 3));
72 return (READ_PORT_UCHAR(port) >> (irq & 7)) & 1;
73 }
74 DPRINT("Broken MPtable reports ISA irq %d\n", irq);
75 return 0;
76 }
77
78 static ULONG
79 IRQPolarity(ULONG idx)
80 {
81 ULONG bus = IRQMap[idx].SrcBusId;
82 ULONG polarity;
83
84 /*
85 * Determine IRQ line polarity (high active or low active):
86 */
87 switch (IRQMap[idx].IrqFlag & 3)
88 {
89 case 0: /* conforms, ie. bus-type dependent polarity */
90 {
91 switch (BUSMap[bus])
92 {
93 case MP_BUS_ISA: /* ISA pin */
94 polarity = default_ISA_polarity(idx);
95 break;
96
97 case MP_BUS_EISA: /* EISA pin */
98 polarity = default_EISA_polarity(idx);
99 break;
100
101 case MP_BUS_PCI: /* PCI pin */
102 polarity = default_PCI_polarity(idx);
103 break;
104
105 case MP_BUS_MCA: /* MCA pin */
106 polarity = default_MCA_polarity(idx);
107 break;
108
109 default:
110 DPRINT("Broken BIOS!!\n");
111 polarity = 1;
112 }
113 }
114 break;
115
116 case 1: /* high active */
117 polarity = 0;
118 break;
119
120 case 2: /* reserved */
121 DPRINT("Broken BIOS!!\n");
122 polarity = 1;
123 break;
124
125 case 3: /* low active */
126 polarity = 1;
127 break;
128
129 default: /* invalid */
130 DPRINT("Broken BIOS!!\n");
131 polarity = 1;
132 }
133 return polarity;
134 }
135
136 static ULONG
137 IRQTrigger(ULONG idx)
138 {
139 ULONG bus = IRQMap[idx].SrcBusId;
140 ULONG trigger;
141
142 /*
143 * Determine IRQ trigger mode (edge or level sensitive):
144 */
145 switch ((IRQMap[idx].IrqFlag >> 2) & 3)
146 {
147 case 0: /* conforms, ie. bus-type dependent */
148 {
149 switch (BUSMap[bus])
150 {
151 case MP_BUS_ISA: /* ISA pin */
152 trigger = default_ISA_trigger(idx);
153 break;
154
155 case MP_BUS_EISA: /* EISA pin */
156 trigger = default_EISA_trigger(idx);
157 break;
158
159 case MP_BUS_PCI: /* PCI pin */
160 trigger = default_PCI_trigger(idx);
161 break;
162
163 case MP_BUS_MCA: /* MCA pin */
164 trigger = default_MCA_trigger(idx);
165 break;
166
167 default:
168 DPRINT("Broken BIOS!!\n");
169 trigger = 1;
170 }
171 }
172 break;
173
174 case 1: /* edge */
175 trigger = 0;
176 break;
177
178 case 2: /* reserved */
179 DPRINT("Broken BIOS!!\n");
180 trigger = 1;
181 break;
182
183 case 3: /* level */
184 trigger = 1;
185 break;
186
187 default: /* invalid */
188 DPRINT("Broken BIOS!!\n");
189 trigger = 0;
190 }
191 return trigger;
192 }
193
194 static ULONG
195 Pin2Irq(ULONG idx,
196 ULONG apic,
197 ULONG pin)
198 {
199 ULONG irq, i;
200 ULONG bus = IRQMap[idx].SrcBusId;
201
202 /*
203 * Debugging check, we are in big trouble if this message pops up!
204 */
205 if (IRQMap[idx].DstApicInt != pin)
206 {
207 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\n");
208 }
209
210 switch (BUSMap[bus])
211 {
212 case MP_BUS_ISA: /* ISA pin */
213 case MP_BUS_EISA:
214 case MP_BUS_MCA:
215 irq = IRQMap[idx].SrcBusIrq;
216 break;
217
218 case MP_BUS_PCI: /* PCI pin */
219 /*
220 * PCI IRQs are mapped in order
221 */
222 i = irq = 0;
223 while (i < apic)
224 {
225 irq += IOAPICMap[i++].EntryCount;
226 }
227 irq += pin;
228 break;
229
230 default:
231 DPRINT("Unknown bus type %d.\n",bus);
232 irq = 0;
233 }
234 return irq;
235 }
236
237 static ULONG
238 AssignIrqVector(ULONG irq)
239 {
240 #if 0
241 static ULONG current_vector = FIRST_DEVICE_VECTOR, vector_offset = 0;
242 #endif
243 ULONG vector;
244 /* There may already have been assigned a vector for this IRQ */
245 vector = IRQVectorMap[irq];
246 if (vector > 0)
247 {
248 return vector;
249 }
250 #if 0
251 if (current_vector > FIRST_SYSTEM_VECTOR)
252 {
253 vector_offset++;
254 current_vector = FIRST_DEVICE_VECTOR + vector_offset;
255 }
256 else if (current_vector == FIRST_SYSTEM_VECTOR)
257 {
258 DPRINT1("Ran out of interrupt sources!");
259 KEBUGCHECK(0);
260 }
261
262 vector = current_vector;
263 IRQVectorMap[irq] = vector;
264 current_vector += 8;
265 return vector;
266 #else
267 vector = IRQ2VECTOR(irq);
268 IRQVectorMap[irq] = vector;
269 return vector;
270 #endif
271 }
272
273 /*
274 * Find the IRQ entry number of a certain pin.
275 */
276 static ULONG
277 IOAPICGetIrqEntry(ULONG apic,
278 ULONG pin,
279 ULONG type)
280 {
281 ULONG i;
282
283 for (i = 0; i < IRQCount; i++)
284 {
285 if (IRQMap[i].IrqType == type &&
286 (IRQMap[i].DstApicId == IOAPICMap[apic].ApicId || IRQMap[i].DstApicId == MP_APIC_ALL) &&
287 IRQMap[i].DstApicInt == pin)
288 {
289 return i;
290 }
291 }
292 return -1;
293 }
294
295
296 VOID
297 IOAPICSetupIrqs(VOID)
298 {
299 IOAPIC_ROUTE_ENTRY entry;
300 ULONG apic, pin, idx, irq, first_notcon = 1, vector, trigger;
301
302 DPRINT("Init IO_APIC IRQs\n");
303
304 /* Setup IRQ to vector translation map */
305 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap));
306
307 for (apic = 0; apic < IOAPICCount; apic++)
308 {
309 for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++)
310 {
311 /*
312 * add it to the IO-APIC irq-routing table
313 */
314 memset(&entry,0,sizeof(entry));
315
316 entry.delivery_mode = (APIC_DM_LOWEST >> 8);
317 entry.dest_mode = 1; /* logical delivery */
318 entry.mask = 1; /* disable IRQ */
319 entry.dest.logical.logical_dest = 0;
320
321 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED);
322 if (idx == (ULONG)-1)
323 {
324 if (first_notcon)
325 {
326 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap[apic].ApicId, pin);
327 first_notcon = 0;
328 }
329 else
330 {
331 DPRINT(", %d-%d\n", IOAPICMap[apic].ApicId, pin);
332 }
333 continue;
334 }
335
336 trigger = IRQTrigger(idx);
337 entry.polarity = IRQPolarity(idx);
338
339 if (trigger)
340 {
341 entry.trigger = 1;
342 }
343
344 irq = Pin2Irq(idx, apic, pin);
345
346 vector = AssignIrqVector(irq);
347 entry.vector = vector;
348
349 DPRINT("vector 0x%.08x assigned to irq 0x%.02x\n", vector, irq);
350
351 if (irq == 0)
352 {
353 /* Mask timer IRQ */
354 entry.mask = 1;
355 }
356
357 if ((apic == 0) && (irq < 16))
358 {
359 Disable8259AIrq(irq);
360 }
361 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1));
362 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0));
363
364 IrqApicMap[irq] = apic;
365
366 DPRINT("Vector %x, Pin %x, Irq %x\n", vector, pin, irq);
367 }
368 }
369 }
370
371 static VOID
372 IOAPICClearPin(ULONG Apic, ULONG Pin)
373 {
374 IOAPIC_ROUTE_ENTRY Entry;
375
376 DPRINT("IOAPICClearPin(Apic %d, Pin %d\n", Apic, Pin);
377 /*
378 * Disable it in the IO-APIC irq-routing table
379 */
380 memset(&Entry, 0, sizeof(Entry));
381 Entry.mask = 1;
382
383 IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0));
384 IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1));
385 }
386
387 static VOID
388 IOAPICClear(ULONG Apic)
389 {
390 ULONG Pin;
391
392 for (Pin = 0; Pin < /*IOAPICMap[Apic].EntryCount*/24; Pin++)
393 {
394 IOAPICClearPin(Apic, Pin);
395 }
396 }
397
398 static VOID
399 IOAPICClearAll(VOID)
400 {
401 ULONG Apic;
402
403 for (Apic = 0; Apic < IOAPICCount; Apic++)
404 {
405 IOAPICClear(Apic);
406 }
407 }
408
409 VOID
410 IOAPICEnable(VOID)
411 {
412 ULONG i, tmp;
413
414 /* Setup IRQ to vector translation map */
415 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap));
416
417 /*
418 * The number of IO-APIC IRQ registers (== #pins):
419 */
420 for (i = 0; i < IOAPICCount; i++)
421 {
422 tmp = IOAPICRead(i, IOAPIC_VER);
423 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1;
424 }
425
426 /*
427 * Do not trust the IO-APIC being empty at bootup
428 */
429 IOAPICClearAll();
430 }
431
432 VOID
433 IOAPICSetupIds(VOID)
434 {
435 ULONG tmp, apic, i;
436 UCHAR old_id;
437
438 /*
439 * Set the IOAPIC ID to the value stored in the MPC table.
440 */
441 for (apic = 0; apic < IOAPICCount; apic++)
442 {
443
444 /* Read the register 0 value */
445 tmp = IOAPICRead(apic, IOAPIC_ID);
446
447 old_id = IOAPICMap[apic].ApicId;
448
449 if (IOAPICMap[apic].ApicId >= 0xf)
450 {
451 DPRINT1("BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",
452 apic, IOAPICMap[apic].ApicId);
453 DPRINT1("... fixing up to %d. (tell your hw vendor)\n",
454 GET_IOAPIC_ID(tmp));
455 IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp);
456 }
457
458 /*
459 * We need to adjust the IRQ routing table
460 * if the ID changed.
461 */
462 if (old_id != IOAPICMap[apic].ApicId)
463 {
464 for (i = 0; i < IRQCount; i++)
465 {
466 if (IRQMap[i].DstApicId == old_id)
467 {
468 IRQMap[i].DstApicId = IOAPICMap[apic].ApicId;
469 }
470 }
471 }
472
473 /*
474 * Read the right value from the MPC table and
475 * write it into the ID register.
476 */
477 DPRINT("Changing IO-APIC physical APIC ID to %d\n",
478 IOAPICMap[apic].ApicId);
479
480 tmp &= ~IOAPIC_ID_MASK;
481 tmp |= SET_IOAPIC_ID(IOAPICMap[apic].ApicId);
482
483 IOAPICWrite(apic, IOAPIC_ID, tmp);
484
485 /*
486 * Sanity check
487 */
488 tmp = IOAPICRead(apic, 0);
489 if (GET_IOAPIC_ID(tmp) != IOAPICMap[apic].ApicId)
490 {
491 DPRINT1("Could not set I/O APIC ID!\n");
492 KEBUGCHECK(0);
493 }
494 }
495 }
496
497 /* This is performance critical and should probably be done in assembler */
498 VOID IOAPICMaskIrq(ULONG Irq)
499 {
500 IOAPIC_ROUTE_ENTRY Entry;
501 ULONG Apic = IrqApicMap[Irq];
502
503 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
504 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1);
505 Entry.dest.logical.logical_dest &= ~(1 << KeGetCurrentProcessorNumber());
506 if (Entry.dest.logical.logical_dest == 0)
507 {
508 Entry.mask = 1;
509 }
510 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1));
511 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0));
512 }
513
514 /* This is performance critical and should probably be done in assembler */
515 VOID IOAPICUnmaskIrq(ULONG Irq)
516 {
517 IOAPIC_ROUTE_ENTRY Entry;
518 ULONG Apic = IrqApicMap[Irq];
519
520 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq);
521 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1);
522 Entry.dest.logical.logical_dest |= 1 << KeGetCurrentProcessorNumber();
523 Entry.mask = 0;
524 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1));
525 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0));
526 }
527
528 VOID IOAPICDump(VOID)
529 {
530 ULONG apic, i;
531 ULONG reg0, reg1, reg2=0;
532
533 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount);
534 for (i = 0; i < IOAPICCount; i++)
535 {
536 DbgPrint("Number of IO-APIC #%d registers: %d.\n",
537 IOAPICMap[i].ApicId,
538 IOAPICMap[i].EntryCount);
539 }
540
541 /*
542 * We are a bit conservative about what we expect. We have to
543 * know about every hardware change ASAP.
544 */
545 DbgPrint("Testing the IO APIC.......................\n");
546
547 for (apic = 0; apic < IOAPICCount; apic++)
548 {
549 reg0 = IOAPICRead(apic, IOAPIC_ID);
550 reg1 = IOAPICRead(apic, IOAPIC_VER);
551 if (GET_IOAPIC_VERSION(reg1) >= 0x10)
552 {
553 reg2 = IOAPICRead(apic, IOAPIC_ARB);
554 }
555
556 DbgPrint("\n");
557 DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId);
558 DbgPrint(".... register #00: %08X\n", reg0);
559 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0));
560 if (reg0 & 0xF0FFFFFF)
561 {
562 DbgPrint(" WARNING: Unexpected IO-APIC\n");
563 }
564
565 DbgPrint(".... register #01: %08X\n", reg1);
566 i = GET_IOAPIC_MRE(reg1);
567
568 DbgPrint("....... : max redirection entries: %04X\n", i);
569 if ((i != 0x0f) && /* older (Neptune) boards */
570 (i != 0x17) && /* typical ISA+PCI boards */
571 (i != 0x1b) && /* Compaq Proliant boards */
572 (i != 0x1f) && /* dual Xeon boards */
573 (i != 0x22) && /* bigger Xeon boards */
574 (i != 0x2E) &&
575 (i != 0x3F))
576 {
577 DbgPrint(" WARNING: Unexpected IO-APIC\n");
578 }
579
580 i = GET_IOAPIC_VERSION(reg1);
581 DbgPrint("....... : IO APIC version: %04X\n", i);
582 if ((i != 0x01) && /* 82489DX IO-APICs */
583 (i != 0x10) && /* oldest IO-APICs */
584 (i != 0x11) && /* Pentium/Pro IO-APICs */
585 (i != 0x13)) /* Xeon IO-APICs */
586 {
587 DbgPrint(" WARNING: Unexpected IO-APIC\n");
588 }
589
590 if (reg1 & 0xFF00FF00)
591 {
592 DbgPrint(" WARNING: Unexpected IO-APIC\n");
593 }
594
595 if (GET_IOAPIC_VERSION(reg1) >= 0x10)
596 {
597 DbgPrint(".... register #02: %08X\n", reg2);
598 DbgPrint("....... : arbitration: %02X\n",
599 GET_IOAPIC_ARB(reg2));
600 if (reg2 & 0xF0FFFFFF)
601 {
602 DbgPrint(" WARNING: Unexpected IO-APIC\n");
603 }
604 }
605
606 DbgPrint(".... IRQ redirection table:\n");
607 DbgPrint(" NR Log Phy Mask Trig IRR Pol"
608 " Stat Dest Deli Vect: \n");
609
610 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++)
611 {
612 IOAPIC_ROUTE_ENTRY entry;
613
614 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2);
615 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2);
616
617 DbgPrint(" %02x %03X %02X ",
618 i,
619 entry.dest.logical.logical_dest,
620 entry.dest.physical.physical_dest);
621
622 DbgPrint("%C %C %1d %C %C %C %03X %02X\n",
623 (entry.mask == 0) ? 'U' : 'M', // Unmasked/masked
624 (entry.trigger == 0) ? 'E' : 'L', // Edge/level sensitive
625 entry.irr,
626 (entry.polarity == 0) ? 'H' : 'L', // Active high/active low
627 (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending
628 (entry.dest_mode == 0) ? 'P' : 'L', // Physical logical
629 entry.delivery_mode,
630 entry.vector);
631 }
632 }
633
634 DbgPrint(".................................... done.\n");
635 }
636
637 VOID
638 HaliReconfigurePciInterrupts(VOID)
639 {
640 ULONG i;
641
642 for (i = 0; i < IRQCount; i++)
643 {
644 if (BUSMap[IRQMap[i].SrcBusId] == MP_BUS_PCI)
645 {
646 DPRINT("%02x: IrqType %02x, IrqFlag %02x, SrcBusId %02x, SrcBusIrq %02x"
647 ", DstApicId %02x, DstApicInt %02x\n",
648 i, IRQMap[i].IrqType, IRQMap[i].IrqFlag, IRQMap[i].SrcBusId,
649 IRQMap[i].SrcBusIrq, IRQMap[i].DstApicId, IRQMap[i].DstApicInt);
650
651 if(1 != HalSetBusDataByOffset(PCIConfiguration,
652 IRQMap[i].SrcBusId,
653 (IRQMap[i].SrcBusIrq >> 2) & 0x1f,
654 &IRQMap[i].DstApicInt,
655 0x3c /*PCI_INTERRUPT_LINE*/,
656 1))
657 {
658 CHECKPOINT;
659 }
660 }
661 }
662 }
663
664 VOID Disable8259AIrq(ULONG irq)
665 {
666 ULONG tmp;
667
668 if (irq >= 8)
669 {
670 tmp = READ_PORT_UCHAR((PUCHAR)0xA1);
671 tmp |= (1 << (irq - 8));
672 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp);
673 }
674 else
675 {
676 tmp = READ_PORT_UCHAR((PUCHAR)0x21);
677 tmp |= (1 << irq);
678 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp);
679 }
680 }
681
682 ULONG IOAPICRead(ULONG Apic, ULONG Offset)
683 {
684 PULONG Base;
685
686 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
687 *Base = Offset;
688 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN));
689 }
690
691 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value)
692 {
693 PULONG Base;
694
695 Base = (PULONG)IOAPICMap[Apic].ApicAddress;
696 *Base = Offset;
697 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value;
698 }
699
700 /* EOF */