[PERF]: Optimize nested interrupt cases (where a pending interrupt exists at the...
[reactos.git] / reactos / hal / halx86 / generic / pic.c
1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: hal/halx86/generic/pic.c
5 * PURPOSE: HAL PIC Management and Control Code
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <hal.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 /*
18 * This table basically keeps track of level vs edge triggered interrupts.
19 * Windows has 250+ entries, but it seems stupid to replicate that since the PIC
20 * can't actually have that many.
21 *
22 * When a level interrupt is registered, the respective pointer in this table is
23 * modified to point to a dimiss routine for level interrupts instead.
24 *
25 * The other thing this table does is special case IRQ7, IRQ13 and IRQ15:
26 *
27 * - If an IRQ line is deasserted before it is acknowledged due to a noise spike
28 * generated by an expansion device (since the IRQ line is low during the 1st
29 * acknowledge bus cycle), the i8259 will keep the line low for at least 100ns
30 * When the spike passes, a pull-up resistor will return the IRQ line to high.
31 * Since the PIC requires the input be high until the first acknowledge, the
32 * i8259 knows that this was a spurious interrupt, and on the second interrupt
33 * acknowledge cycle, it reports this to the CPU. Since no valid interrupt has
34 * actually happened Intel hardcoded the chip to report IRQ7 on the master PIC
35 * and IRQ15 on the slave PIC (IR7 either way).
36 *
37 * "ISA System Architecture", 3rd Edition, states that these cases should be
38 * handled by reading the respective Interrupt Service Request (ISR) bits from
39 * the affected PIC, and validate whether or not IR7 is set. If it isn't, then
40 * the interrupt is spurious and should be ignored.
41 *
42 * Note that for a spurious IRQ15, we DO have to send an EOI to the master for
43 * IRQ2 since the line was asserted by the slave when it received the spurious
44 * IRQ15!
45 *
46 * - When the 80287/80387 math co-processor generates an FPU/NPX trap, this is
47 * connected to IRQ13, so we have to clear the busy latch on the NPX port.
48 */
49 PHAL_DISMISS_INTERRUPT HalpSpecialDismissTable[16] =
50 {
51 HalpDismissIrqGeneric,
52 HalpDismissIrqGeneric,
53 HalpDismissIrqGeneric,
54 HalpDismissIrqGeneric,
55 HalpDismissIrqGeneric,
56 HalpDismissIrqGeneric,
57 HalpDismissIrqGeneric,
58 HalpDismissIrq07,
59 HalpDismissIrqGeneric,
60 HalpDismissIrqGeneric,
61 HalpDismissIrqGeneric,
62 HalpDismissIrqGeneric,
63 HalpDismissIrqGeneric,
64 HalpDismissIrq13,
65 HalpDismissIrqGeneric,
66 HalpDismissIrq15
67 };
68
69 /* This table contains the static x86 PIC mapping between IRQLs and IRQs */
70 ULONG KiI8259MaskTable[32] =
71 {
72 #ifdef __GNUC__
73 #if __GNUC__ * 100 + __GNUC_MINOR__ >= 404
74 /*
75 * It Device IRQLs only start at 4 or higher, so these are just software
76 * IRQLs that don't really change anything on the hardware
77 */
78 0b00000000000000000000000000000000, /* IRQL 0 */
79 0b00000000000000000000000000000000, /* IRQL 1 */
80 0b00000000000000000000000000000000, /* IRQL 2 */
81 0b00000000000000000000000000000000, /* IRQL 3 */
82
83 /*
84 * These next IRQLs are actually useless from the PIC perspective, because
85 * with only 2 PICs, the mask you can send them is only 8 bits each, for 16
86 * bits total, so these IRQLs are masking off a phantom PIC.
87 */
88 0b11111111100000000000000000000000, /* IRQL 4 */
89 0b11111111110000000000000000000000, /* IRQL 5 */
90 0b11111111111000000000000000000000, /* IRQL 6 */
91 0b11111111111100000000000000000000, /* IRQL 7 */
92 0b11111111111110000000000000000000, /* IRQL 8 */
93 0b11111111111111000000000000000000, /* IRQL 9 */
94 0b11111111111111100000000000000000, /* IRQL 10 */
95 0b11111111111111110000000000000000, /* IRQL 11 */
96
97 /*
98 * Okay, now we're finally starting to mask off IRQs on the slave PIC, from
99 * IRQ15 to IRQ8. This means the higher-level IRQs get less priority in the
100 * IRQL sense.
101 */
102 0b11111111111111111000000000000000, /* IRQL 12 */
103 0b11111111111111111100000000000000, /* IRQL 13 */
104 0b11111111111111111110000000000000, /* IRQL 14 */
105 0b11111111111111111111000000000000, /* IRQL 15 */
106 0b11111111111111111111100000000000, /* IRQL 16 */
107 0b11111111111111111111110000000000, /* IRQL 17 */
108 0b11111111111111111111111000000000, /* IRQL 18 */
109 0b11111111111111111111111000000000, /* IRQL 19 */
110
111 /*
112 * Now we mask off the IRQs on the master. Notice the 0 "droplet"? You might
113 * have also seen that IRQL 18 and 19 are essentially equal as far as the
114 * PIC is concerned. That bit is actually IRQ8, which happens to be the RTC.
115 * The RTC will keep firing as long as we don't reach PROFILE_LEVEL which
116 * actually kills it. The RTC clock (unlike the system clock) is used by the
117 * profiling APIs in the HAL, so that explains the logic.
118 */
119 0b11111111111111111111111010000000, /* IRQL 20 */
120 0b11111111111111111111111011000000, /* IRQL 21 */
121 0b11111111111111111111111011100000, /* IRQL 22 */
122 0b11111111111111111111111011110000, /* IRQL 23 */
123 0b11111111111111111111111011111000, /* IRQL 24 */
124 0b11111111111111111111111011111000, /* IRQL 25 */
125 0b11111111111111111111111011111010, /* IRQL 26 */
126 0b11111111111111111111111111111010, /* IRQL 27 */
127
128 /*
129 * IRQL 24 and 25 are actually identical, so IRQL 28 is actually the last
130 * IRQL to modify a bit on the master PIC. It happens to modify the very
131 * last of the IRQs, IRQ0, which corresponds to the system clock interval
132 * timer that keeps track of time (the Windows heartbeat). We only want to
133 * turn this off at a high-enough IRQL, which is why IRQLs 24 and 25 are the
134 * same to give this guy a chance to come up higher. Note that IRQL 28 is
135 * called CLOCK2_LEVEL, which explains the usage we just explained.
136 */
137 0b11111111111111111111111111111011, /* IRQL 28 */
138
139 /*
140 * We have finished off with the PIC so there's nothing left to mask at the
141 * level of these IRQLs, making them only logical IRQLs on x86 machines.
142 * Note that we have another 0 "droplet" you might've caught since IRQL 26.
143 * In this case, it's the 2nd bit that never gets turned off, which is IRQ2,
144 * the cascade IRQ that we use to bridge the slave PIC with the master PIC.
145 * We never want to turn it off, so no matter the IRQL, it will be set to 0.
146 */
147 0b11111111111111111111111111111011, /* IRQL 29 */
148 0b11111111111111111111111111111011, /* IRQL 30 */
149 0b11111111111111111111111111111011 /* IRQL 31 */
150 #else
151 0, /* IRQL 0 */
152 0, /* IRQL 1 */
153 0, /* IRQL 2 */
154 0, /* IRQL 3 */
155 0xFF800000, /* IRQL 4 */
156 0xFFC00000, /* IRQL 5 */
157 0xFFE00000, /* IRQL 6 */
158 0xFFF00000, /* IRQL 7 */
159 0xFFF80000, /* IRQL 8 */
160 0xFFFC0000, /* IRQL 9 */
161 0xFFFE0000, /* IRQL 10 */
162 0xFFFF0000, /* IRQL 11 */
163 0xFFFF8000, /* IRQL 12 */
164 0xFFFFC000, /* IRQL 13 */
165 0xFFFFE000, /* IRQL 14 */
166 0xFFFFF000, /* IRQL 15 */
167 0xFFFFF800, /* IRQL 16 */
168 0xFFFFFC00, /* IRQL 17 */
169 0xFFFFFE00, /* IRQL 18 */
170 0xFFFFFE00, /* IRQL 19 */
171 0xFFFFFE80, /* IRQL 20 */
172 0xFFFFFEC0, /* IRQL 21 */
173 0xFFFFFEE0, /* IRQL 22 */
174 0xFFFFFEF0, /* IRQL 23 */
175 0xFFFFFEF8, /* IRQL 24 */
176 0xFFFFFEF8, /* IRQL 25 */
177 0xFFFFFEFA, /* IRQL 26 */
178 0xFFFFFFFA, /* IRQL 27 */
179 0xFFFFFFFB, /* IRQL 28 */
180 0xFFFFFFFB, /* IRQL 29 */
181 0xFFFFFFFB, /* IRQL 30 */
182 0xFFFFFFFB /* IRQL 31 */
183 #endif
184 #endif
185 };
186
187 /* Denotes minimum required IRQL before we can process pending SW interrupts */
188 KIRQL SWInterruptLookUpTable[8] =
189 {
190 PASSIVE_LEVEL, /* IRR 0 */
191 PASSIVE_LEVEL, /* IRR 1 */
192 APC_LEVEL, /* IRR 2 */
193 APC_LEVEL, /* IRR 3 */
194 DISPATCH_LEVEL, /* IRR 4 */
195 DISPATCH_LEVEL, /* IRR 5 */
196 DISPATCH_LEVEL, /* IRR 6 */
197 DISPATCH_LEVEL /* IRR 7 */
198 };
199
200 /* Handlers for pending software interrupts */
201 PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[3] =
202 {
203 KiUnexpectedInterrupt,
204 HalpApcInterrupt,
205 HalpDispatchInterrupt
206 };
207
208 /* Handlers for pending software interrupts when we already have a trap frame*/
209 PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY SWInterruptHandlerTable2[3] =
210 {
211 (PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY)KiUnexpectedInterrupt,
212 HalpApcInterrupt2ndEntry,
213 HalpDispatchInterrupt2ndEntry
214 };
215
216
217 USHORT HalpEisaELCR;
218
219 /* FUNCTIONS ******************************************************************/
220
221 VOID
222 NTAPI
223 HalpInitializePICs(IN BOOLEAN EnableInterrupts)
224 {
225 ULONG EFlags;
226 I8259_ICW1 Icw1;
227 I8259_ICW2 Icw2;
228 I8259_ICW3 Icw3;
229 I8259_ICW4 Icw4;
230 EISA_ELCR Elcr;
231 ULONG i, j;
232
233 /* Save EFlags and disable interrupts */
234 EFlags = __readeflags();
235 _disable();
236
237 /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
238 Icw1.NeedIcw4 = TRUE;
239 Icw1.InterruptMode = EdgeTriggered;
240 Icw1.OperatingMode = Cascade;
241 Icw1.Interval = Interval8;
242 Icw1.Init = TRUE;
243 Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
244 __outbyte(PIC1_CONTROL_PORT, Icw1.Bits);
245
246 /* Set interrupt vector base */
247 Icw2.Bits = PRIMARY_VECTOR_BASE;
248 __outbyte(PIC1_DATA_PORT, Icw2.Bits);
249
250 /* Connect slave to IRQ 2 */
251 Icw3.Bits = 0;
252 Icw3.SlaveIrq2 = TRUE;
253 __outbyte(PIC1_DATA_PORT, Icw3.Bits);
254
255 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
256 Icw4.Reserved = 0;
257 Icw4.SystemMode = New8086Mode;
258 Icw4.EoiMode = NormalEoi;
259 Icw4.BufferedMode = NonBuffered;
260 Icw4.SpecialFullyNestedMode = FALSE;
261 __outbyte(PIC1_DATA_PORT, Icw4.Bits);
262
263 /* Mask all interrupts */
264 __outbyte(PIC1_DATA_PORT, 0xFF);
265
266 /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
267 Icw1.NeedIcw4 = TRUE;
268 Icw1.InterruptMode = EdgeTriggered;
269 Icw1.OperatingMode = Cascade;
270 Icw1.Interval = Interval8;
271 Icw1.Init = TRUE;
272 Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
273 __outbyte(PIC2_CONTROL_PORT, Icw1.Bits);
274
275 /* Set interrupt vector base */
276 Icw2.Bits = PRIMARY_VECTOR_BASE + 8;
277 __outbyte(PIC2_DATA_PORT, Icw2.Bits);
278
279 /* Slave ID */
280 Icw3.Bits = 0;
281 Icw3.SlaveId = 2;
282 __outbyte(PIC2_DATA_PORT, Icw3.Bits);
283
284 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
285 Icw4.Reserved = 0;
286 Icw4.SystemMode = New8086Mode;
287 Icw4.EoiMode = NormalEoi;
288 Icw4.BufferedMode = NonBuffered;
289 Icw4.SpecialFullyNestedMode = FALSE;
290 __outbyte(PIC2_DATA_PORT, Icw4.Bits);
291
292 /* Mask all interrupts */
293 __outbyte(PIC2_DATA_PORT, 0xFF);
294
295 /* Read EISA Edge/Level Register for master and slave */
296 Elcr.Bits = (__inbyte(EISA_ELCR_SLAVE) << 8) | __inbyte(EISA_ELCR_MASTER);
297
298 /* IRQs 0, 1, 2, 8, and 13 are system-reserved and must be edge */
299 if (!(Elcr.Master.Irq0Level) && !(Elcr.Master.Irq1Level) && !(Elcr.Master.Irq2Level) &&
300 !(Elcr.Slave.Irq8Level) && !(Elcr.Slave.Irq13Level))
301 {
302 /* ELCR is as it's supposed to be, save it */
303 HalpEisaELCR = Elcr.Bits;
304 DPRINT1("HAL Detected EISA Interrupt Controller (ELCR: %lx)\n", HalpEisaELCR);
305
306 /* Scan for level interrupts */
307 for (i = 1, j = 0; j < 16; i <<= 1, j++)
308 {
309 /* Warn the user ReactOS does not (and has never) supported this */
310 if (HalpEisaELCR & i) DPRINT1("WARNING: IRQ %d is SHARED and LEVEL-SENSITIVE. This is unsupported!\n", j);
311 }
312 }
313
314 /* Restore interrupt state */
315 if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
316 __writeeflags(EFlags);
317 }
318
319 /* IRQL MANAGEMENT ************************************************************/
320
321 /*
322 * @implemented
323 */
324 KIRQL
325 NTAPI
326 KeGetCurrentIrql(VOID)
327 {
328 /* Return the IRQL */
329 return KeGetPcr()->Irql;
330 }
331
332 /*
333 * @implemented
334 */
335 KIRQL
336 NTAPI
337 KeRaiseIrqlToDpcLevel(VOID)
338 {
339 PKPCR Pcr = KeGetPcr();
340 KIRQL CurrentIrql;
341
342 /* Save and update IRQL */
343 CurrentIrql = Pcr->Irql;
344 Pcr->Irql = DISPATCH_LEVEL;
345
346 #ifdef IRQL_DEBUG
347 /* Validate correct raise */
348 if (CurrentIrql > DISPATCH_LEVEL)
349 {
350 /* Crash system */
351 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
352 CurrentIrql,
353 DISPATCH_LEVEL,
354 0,
355 1);
356 }
357 #endif
358
359 /* Return the previous value */
360 return CurrentIrql;
361 }
362
363 /*
364 * @implemented
365 */
366 KIRQL
367 NTAPI
368 KeRaiseIrqlToSynchLevel(VOID)
369 {
370 PKPCR Pcr = KeGetPcr();
371 KIRQL CurrentIrql;
372
373 /* Save and update IRQL */
374 CurrentIrql = Pcr->Irql;
375 Pcr->Irql = SYNCH_LEVEL;
376
377 #ifdef IRQL_DEBUG
378 /* Validate correct raise */
379 if (CurrentIrql > SYNCH_LEVEL)
380 {
381 /* Crash system */
382 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
383 CurrentIrql,
384 SYNCH_LEVEL,
385 0,
386 1);
387 }
388 #endif
389
390 /* Return the previous value */
391 return CurrentIrql;
392 }
393
394 /*
395 * @implemented
396 */
397 KIRQL
398 FASTCALL
399 KfRaiseIrql(IN KIRQL NewIrql)
400 {
401 PKPCR Pcr = KeGetPcr();
402 ULONG EFlags;
403 KIRQL CurrentIrql;
404 PIC_MASK Mask;
405
406 /* Read current IRQL */
407 CurrentIrql = Pcr->Irql;
408
409 #ifdef IRQL_DEBUG
410 /* Validate correct raise */
411 if (CurrentIrql > NewIrql)
412 {
413 /* Crash system */
414 Pcr->Irql = PASSIVE_LEVEL;
415 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
416 CurrentIrql,
417 NewIrql,
418 0,
419 9);
420 }
421 #endif
422
423 /* Check if new IRQL affects hardware state */
424 if (NewIrql > DISPATCH_LEVEL)
425 {
426 /* Save current interrupt state and disable interrupts */
427 EFlags = __readeflags();
428 _disable();
429
430 /* Update the IRQL */
431 Pcr->Irql = NewIrql;
432
433 /* Set new PIC mask */
434 Mask.Both = KiI8259MaskTable[NewIrql] | Pcr->IDR;
435 __outbyte(PIC1_DATA_PORT, Mask.Master);
436 __outbyte(PIC2_DATA_PORT, Mask.Slave);
437
438 /* Restore interrupt state */
439 __writeeflags(EFlags);
440 }
441 else
442 {
443 /* Set new IRQL */
444 Pcr->Irql = NewIrql;
445 }
446
447 /* Return old IRQL */
448 return CurrentIrql;
449 }
450
451
452 /*
453 * @implemented
454 */
455 VOID
456 FASTCALL
457 KfLowerIrql(IN KIRQL OldIrql)
458 {
459 ULONG EFlags;
460 KIRQL PendingIrql;
461 PKPCR Pcr = KeGetPcr();
462 PIC_MASK Mask;
463
464 #ifdef IRQL_DEBUG
465 /* Validate correct lower */
466 if (OldIrql > Pcr->Irql)
467 {
468 /* Crash system */
469 KIRQL CurrentIrql = Pcr->Irql;
470 Pcr->Irql = HIGH_LEVEL;
471 KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL,
472 CurrentIrql,
473 OldIrql,
474 0,
475 3);
476 }
477 #endif
478
479 /* Save EFlags and disable interrupts */
480 EFlags = __readeflags();
481 _disable();
482
483 /* Check if currentl IRQL affects hardware state */
484 if (Pcr->Irql > DISPATCH_LEVEL)
485 {
486 /* Set new PIC mask */
487 Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
488 __outbyte(PIC1_DATA_PORT, Mask.Master);
489 __outbyte(PIC2_DATA_PORT, Mask.Slave);
490 }
491
492 /* Set old IRQL */
493 Pcr->Irql = OldIrql;
494
495 /* Check for pending software interrupts and compare with current IRQL */
496 PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
497 if (PendingIrql > OldIrql) SWInterruptHandlerTable[PendingIrql]();
498
499 /* Restore interrupt state */
500 __writeeflags(EFlags);
501 }
502
503 /* SOFTWARE INTERRUPTS ********************************************************/
504
505 /*
506 * @implemented
507 */
508 VOID
509 FASTCALL
510 HalRequestSoftwareInterrupt(IN KIRQL Irql)
511 {
512 ULONG EFlags;
513 PKPCR Pcr = KeGetPcr();
514 KIRQL PendingIrql;
515
516 /* Save EFlags and disable interrupts */
517 EFlags = __readeflags();
518 _disable();
519
520 /* Mask out the requested bit */
521 Pcr->IRR |= (1 << Irql);
522
523 /* Check for pending software interrupts and compare with current IRQL */
524 PendingIrql = SWInterruptLookUpTable[Pcr->IRR & 3];
525 if (PendingIrql > Pcr->Irql) SWInterruptHandlerTable[PendingIrql]();
526
527 /* Restore interrupt state */
528 __writeeflags(EFlags);
529 }
530
531 /*
532 * @implemented
533 */
534 VOID
535 FASTCALL
536 HalClearSoftwareInterrupt(IN KIRQL Irql)
537 {
538 /* Mask out the requested bit */
539 KeGetPcr()->IRR &= ~(1 << Irql);
540 }
541
542 VOID
543 NTAPI
544 HalpEndSoftwareInterrupt(IN KIRQL OldIrql)
545 {
546 KIRQL PendingIrql;
547 PKPCR Pcr = KeGetPcr();
548 PIC_MASK Mask;
549
550 /* Check if currentl IRQL affects hardware state */
551 if (Pcr->Irql > DISPATCH_LEVEL)
552 {
553 /* Set new PIC mask */
554 Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
555 __outbyte(PIC1_DATA_PORT, Mask.Master);
556 __outbyte(PIC2_DATA_PORT, Mask.Slave);
557 }
558
559 /* Set old IRQL */
560 Pcr->Irql = OldIrql;
561
562 /* Check for pending software interrupts and compare with current IRQL */
563 PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
564 if (PendingIrql > OldIrql) HalpNestedTrap(PendingIrql);
565 }
566
567 /* INTERRUPT DISMISSAL FUNCTIONS **********************************************/
568
569 BOOLEAN
570 FORCEINLINE
571 _HalpDismissIrqGeneric(IN KIRQL Irql,
572 IN ULONG Irq,
573 OUT PKIRQL OldIrql)
574 {
575 PIC_MASK Mask;
576 KIRQL CurrentIrql;
577 I8259_OCW2 Ocw2;
578 PKPCR Pcr = KeGetPcr();
579
580 /* First save current IRQL and compare it to the requested one */
581 CurrentIrql = Pcr->Irql;
582
583 /* Set the new IRQL and return the current one */
584 Pcr->Irql = Irql;
585 *OldIrql = CurrentIrql;
586
587 /* Set new PIC mask */
588 Mask.Both = KiI8259MaskTable[Irql] | Pcr->IDR;
589 __outbyte(PIC1_DATA_PORT, Mask.Master);
590 __outbyte(PIC2_DATA_PORT, Mask.Slave);
591
592 /* Prepare OCW2 for EOI */
593 Ocw2.Bits = 0;
594 Ocw2.EoiMode = SpecificEoi;
595
596 /* Check which PIC needs the EOI */
597 if (Irq > 8)
598 {
599 /* Send the EOI for the IRQ */
600 __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | (Irq - 8));
601
602 /* Send the EOI for IRQ2 on the master because this was cascaded */
603 __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
604 }
605 else
606 {
607 /* Send the EOI for the IRQ */
608 __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | Irq);
609 }
610
611 /* Enable interrupts and return success */
612 _enable();
613 return TRUE;
614 }
615
616 BOOLEAN
617 __attribute__((regparm(3)))
618 HalpDismissIrqGeneric(IN KIRQL Irql,
619 IN ULONG Irq,
620 OUT PKIRQL OldIrql)
621 {
622 /* Run the inline code */
623 return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
624 }
625
626 BOOLEAN
627 __attribute__((regparm(3)))
628 HalpDismissIrq15(IN KIRQL Irql,
629 IN ULONG Irq,
630 OUT PKIRQL OldIrql)
631 {
632 I8259_OCW3 Ocw3;
633 I8259_OCW2 Ocw2;
634 I8259_ISR Isr;
635
636 /* Request the ISR */
637 Ocw3.Bits = 0;
638 Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
639 Ocw3.ReadRequest = ReadIsr;
640 __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
641
642 /* Read the ISR */
643 Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
644
645 /* Is IRQ15 really active (this is IR7) */
646 if (Isr.Irq7 == FALSE)
647 {
648 /* It isn't, so we have to EOI IRQ2 because this was cascaded */
649 Ocw2.Bits = 0;
650 Ocw2.EoiMode = SpecificEoi;
651 __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
652
653 /* And now fail since this was spurious */
654 return FALSE;
655 }
656
657 /* Do normal interrupt dismiss */
658 return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
659 }
660
661
662 BOOLEAN
663 __attribute__((regparm(3)))
664 HalpDismissIrq13(IN KIRQL Irql,
665 IN ULONG Irq,
666 OUT PKIRQL OldIrql)
667 {
668 /* Clear the FPU busy latch */
669 __outbyte(0xF0, 0);
670
671 /* Do normal interrupt dismiss */
672 return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
673 }
674
675 BOOLEAN
676 __attribute__((regparm(3)))
677 HalpDismissIrq07(IN KIRQL Irql,
678 IN ULONG Irq,
679 OUT PKIRQL OldIrql)
680 {
681 I8259_OCW3 Ocw3;
682 I8259_ISR Isr;
683
684 /* Request the ISR */
685 Ocw3.Bits = 0;
686 Ocw3.Sbo = 1;
687 Ocw3.ReadRequest = ReadIsr;
688 __outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);
689
690 /* Read the ISR */
691 Isr.Bits = __inbyte(PIC1_CONTROL_PORT);
692
693 /* Is IRQ 7 really active? If it isn't, this is spurious so fail */
694 if (Isr.Irq7 == FALSE) return FALSE;
695
696 /* Do normal interrupt dismiss */
697 return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
698 }
699
700 /* SYSTEM INTERRUPTS **********************************************************/
701
702 /*
703 * @implemented
704 */
705 BOOLEAN
706 NTAPI
707 HalEnableSystemInterrupt(IN UCHAR Vector,
708 IN KIRQL Irql,
709 IN KINTERRUPT_MODE InterruptMode)
710 {
711 ULONG Irq;
712 PKPCR Pcr = KeGetPcr();
713 PIC_MASK PicMask;
714
715 /* Validate the IRQ */
716 Irq = Vector - PRIMARY_VECTOR_BASE;
717 if (Irq >= CLOCK2_LEVEL) return FALSE;
718
719 #ifdef PCI_IRQ_MP
720 /* Check if there is a PCI IRQ Routing Miniport Driver */
721 if (HalpIrqMiniportInitialized)
722 {
723 UNIMPLEMENTED;
724 while (TRUE);
725 }
726 #endif
727
728 /* Disable interrupts */
729 _disable();
730
731 /* Update software IDR */
732 Pcr->IDR &= ~(1 << Irq);
733
734 /* Set new PIC mask */
735 PicMask.Both = KiI8259MaskTable[Pcr->Irql] | Pcr->IDR;
736 __outbyte(PIC1_DATA_PORT, PicMask.Master);
737 __outbyte(PIC2_DATA_PORT, PicMask.Slave);
738
739 /* Enable interrupts and exit */
740 _enable();
741 return TRUE;
742 }
743
744 /*
745 * @implemented
746 */
747 VOID
748 NTAPI
749 HalDisableSystemInterrupt(IN UCHAR Vector,
750 IN KIRQL Irql)
751 {
752 ULONG IrqMask;
753 PIC_MASK PicMask;
754
755 /* Compute new combined IRQ mask */
756 IrqMask = 1 << (Vector - PRIMARY_VECTOR_BASE);
757
758 /* Disable interrupts */
759 _disable();
760
761 /* Update software IDR */
762 KeGetPcr()->IDR |= IrqMask;
763
764 /* Read current interrupt mask */
765 PicMask.Master = __inbyte(PIC1_DATA_PORT);
766 PicMask.Slave = __inbyte(PIC2_DATA_PORT);
767
768 /* Add the new disabled interrupt */
769 PicMask.Both |= IrqMask;
770
771 /* Write new interrupt mask */
772 __outbyte(PIC1_DATA_PORT, PicMask.Master);
773 __outbyte(PIC2_DATA_PORT, PicMask.Slave);
774
775 /* Bring interrupts back */
776 _enable();
777 }
778
779 /*
780 * @implemented
781 */
782 BOOLEAN
783 NTAPI
784 HalBeginSystemInterrupt(IN KIRQL Irql,
785 IN UCHAR Vector,
786 OUT PKIRQL OldIrql)
787 {
788 ULONG Irq;
789
790 /* Get the IRQ and call the proper routine to handle it */
791 Irq = Vector - PRIMARY_VECTOR_BASE;
792 return HalpSpecialDismissTable[Irq](Irql, Irq, OldIrql);
793 }
794
795 /*
796 * @implemented
797 */
798 VOID
799 NTAPI
800 HalEndSystemInterrupt(IN KIRQL OldIrql,
801 IN UCHAR Vector)
802 {
803 KIRQL PendingIrql;
804 PKPCR Pcr = KeGetPcr();
805 PIC_MASK Mask;
806
807 /* Check if currentl IRQL affects hardware state */
808 if (Pcr->Irql > DISPATCH_LEVEL)
809 {
810 /* Set new PIC mask */
811 Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
812 __outbyte(PIC1_DATA_PORT, Mask.Master);
813 __outbyte(PIC2_DATA_PORT, Mask.Slave);
814 }
815
816 /* Set old IRQL */
817 Pcr->Irql = OldIrql;
818
819 /* Check for pending software interrupts and compare with current IRQL */
820 PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
821 if (PendingIrql > OldIrql) HalpNestedTrap(PendingIrql);
822 }
823
824 /* SOFTWARE INTERRUPT TRAPS ***************************************************/
825
826 VOID
827 FORCEINLINE
828 DECLSPEC_NORETURN
829 _HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
830 {
831 KIRQL CurrentIrql;
832 PKPCR Pcr = KeGetPcr();
833
834 /* Save the current IRQL and update it */
835 CurrentIrql = Pcr->Irql;
836 Pcr->Irql = APC_LEVEL;
837
838 /* Remove DPC from IRR */
839 Pcr->IRR &= ~(1 << APC_LEVEL);
840
841 /* Enable interrupts and call the kernel's APC interrupt handler */
842 _enable();
843 KiDeliverApc(((KiUserTrap(TrapFrame)) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) ?
844 UserMode : KernelMode,
845 NULL,
846 TrapFrame);
847
848 /* Disable interrupts and end the interrupt */
849 _disable();
850 Pcr->VdmAlert = (ULONG_PTR)TrapFrame;
851 HalpEndSoftwareInterrupt(CurrentIrql);
852
853 /* Exit the interrupt */
854 KiEoiHelper(TrapFrame);
855 }
856
857 VOID
858 FASTCALL
859 DECLSPEC_NORETURN
860 HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
861 {
862 /* Do the work */
863 _HalpApcInterruptHandler(TrapFrame);
864 }
865
866 VOID
867 FASTCALL
868 DECLSPEC_NORETURN
869 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
870 {
871 /* Set up a fake INT Stack */
872 TrapFrame->EFlags = __readeflags();
873 TrapFrame->SegCs = KGDT_R0_CODE;
874 TrapFrame->Eip = TrapFrame->Eax;
875
876 /* Build the trap frame */
877 KiEnterInterruptTrap(TrapFrame);
878
879 /* Do the work */
880 _HalpApcInterruptHandler(TrapFrame);
881 }
882
883 VOID
884 FORCEINLINE
885 DECLSPEC_NORETURN
886 _HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
887 {
888 KIRQL CurrentIrql;
889 PKPCR Pcr = KeGetPcr();
890
891 /* Save the current IRQL and update it */
892 CurrentIrql = Pcr->Irql;
893 Pcr->Irql = DISPATCH_LEVEL;
894
895 /* Remove DPC from IRR */
896 Pcr->IRR &= ~(1 << DISPATCH_LEVEL);
897
898 /* Enable interrupts and call the kernel's DPC interrupt handler */
899 _enable();
900 KiDispatchInterrupt();
901
902 /* Disable interrupts and end the interrupt */
903 _disable();
904 Pcr->VdmAlert = (ULONG_PTR)TrapFrame;
905 HalpEndSoftwareInterrupt(CurrentIrql);
906
907 /* Exit the interrupt */
908 KiEoiHelper(TrapFrame);
909 }
910
911 VOID
912 FASTCALL
913 DECLSPEC_NORETURN
914 HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
915 {
916 /* Do the work */
917 _HalpDispatchInterruptHandler(TrapFrame);
918 }
919
920 VOID
921 FASTCALL
922 DECLSPEC_NORETURN
923 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
924 {
925 /* Set up a fake INT Stack */
926 TrapFrame->EFlags = __readeflags();
927 TrapFrame->SegCs = KGDT_R0_CODE;
928 TrapFrame->Eip = TrapFrame->Eax;
929
930 /* Build the trap frame */
931 KiEnterInterruptTrap(TrapFrame);
932
933 /* Do the work */
934 _HalpDispatchInterruptHandler(TrapFrame);
935 }
936
937 KiTrap(HalpApcInterrupt, KI_SOFTWARE_TRAP);
938 KiTrap(HalpDispatchInterrupt, KI_SOFTWARE_TRAP);