xref: /reactos/hal/halx86/pic/pic.c (revision 9d33a205)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * PURPOSE:         HAL PIC Management and Control Code
5  * PROGRAMMERS:     ReactOS Portable Systems Group
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <hal.h>
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 VOID
16 NTAPI
17 HalpEndSoftwareInterrupt(IN KIRQL OldIrql,
18                          IN PKTRAP_FRAME TrapFrame);
19 
20 /* GLOBALS ********************************************************************/
21 
22 #ifndef _MINIHAL_
23 /*
24  * This table basically keeps track of level vs edge triggered interrupts.
25  * Windows has 250+ entries, but it seems stupid to replicate that since the PIC
26  * can't actually have that many.
27  *
28  * When a level interrupt is registered, the respective pointer in this table is
29  * modified to point to a dimiss routine for level interrupts instead.
30  *
31  * The other thing this table does is special case IRQ7, IRQ13 and IRQ15:
32  *
33  * - If an IRQ line is deasserted before it is acknowledged due to a noise spike
34  *   generated by an expansion device (since the IRQ line is low during the 1st
35  *   acknowledge bus cycle), the i8259 will keep the line low for at least 100ns
36  *   When the spike passes, a pull-up resistor will return the IRQ line to high.
37  *   Since the PIC requires the input be high until the first acknowledge, the
38  *   i8259 knows that this was a spurious interrupt, and on the second interrupt
39  *   acknowledge cycle, it reports this to the CPU. Since no valid interrupt has
40  *   actually happened Intel hardcoded the chip to report IRQ7 on the master PIC
41  *   and IRQ15 on the slave PIC (IR7 either way).
42  *
43  *   "ISA System Architecture", 3rd Edition, states that these cases should be
44  *   handled by reading the respective Interrupt Service Request (ISR) bits from
45  *   the affected PIC, and validate whether or not IR7 is set. If it isn't, then
46  *   the interrupt is spurious and should be ignored.
47  *
48  *   Note that for a spurious IRQ15, we DO have to send an EOI to the master for
49  *   IRQ2 since the line was asserted by the slave when it received the spurious
50  *   IRQ15!
51  *
52  * - When the 80287/80387 math co-processor generates an FPU/NPX trap, this is
53  *   connected to IRQ13, so we have to clear the busy latch on the NPX port.
54  */
55 PHAL_DISMISS_INTERRUPT HalpSpecialDismissTable[16] =
56 {
57     HalpDismissIrqGeneric,
58     HalpDismissIrqGeneric,
59     HalpDismissIrqGeneric,
60     HalpDismissIrqGeneric,
61     HalpDismissIrqGeneric,
62     HalpDismissIrqGeneric,
63     HalpDismissIrqGeneric,
64     HalpDismissIrq07,
65 #if defined(SARCH_PC98)
66     HalpDismissIrq08,
67 #else
68     HalpDismissIrqGeneric,
69 #endif
70     HalpDismissIrqGeneric,
71     HalpDismissIrqGeneric,
72     HalpDismissIrqGeneric,
73     HalpDismissIrqGeneric,
74 #if defined(SARCH_PC98)
75     HalpDismissIrqGeneric,
76 #else
77     HalpDismissIrq13,
78 #endif
79     HalpDismissIrqGeneric,
80     HalpDismissIrq15
81 };
82 
83 /*
84  * These are the level IRQ dismissal functions that get copied in the table
85  * above if the given IRQ is actually level triggered.
86  */
87 PHAL_DISMISS_INTERRUPT HalpSpecialDismissLevelTable[16] =
88 {
89     HalpDismissIrqLevel,
90     HalpDismissIrqLevel,
91     HalpDismissIrqLevel,
92     HalpDismissIrqLevel,
93     HalpDismissIrqLevel,
94     HalpDismissIrqLevel,
95     HalpDismissIrqLevel,
96     HalpDismissIrq07Level,
97 #if defined(SARCH_PC98)
98     HalpDismissIrq08Level,
99 #else
100     HalpDismissIrqLevel,
101 #endif
102     HalpDismissIrqLevel,
103     HalpDismissIrqLevel,
104     HalpDismissIrqLevel,
105     HalpDismissIrqLevel,
106 #if defined(SARCH_PC98)
107     HalpDismissIrqLevel,
108 #else
109     HalpDismissIrq13Level,
110 #endif
111     HalpDismissIrqLevel,
112     HalpDismissIrq15Level
113 };
114 
115 /* This table contains the static x86 PIC mapping between IRQLs and IRQs */
116 extern ULONG KiI8259MaskTable[32];
117 
118 /* This table indicates which IRQs, if pending, can preempt a given IRQL level */
119 extern ULONG FindHigherIrqlMask[32];
120 
121 /* Denotes minimum required IRQL before we can process pending SW interrupts */
122 KIRQL SWInterruptLookUpTable[8] =
123 {
124     PASSIVE_LEVEL,                 /* IRR 0 */
125     PASSIVE_LEVEL,                 /* IRR 1 */
126     APC_LEVEL,                     /* IRR 2 */
127     APC_LEVEL,                     /* IRR 3 */
128     DISPATCH_LEVEL,                /* IRR 4 */
129     DISPATCH_LEVEL,                /* IRR 5 */
130     DISPATCH_LEVEL,                /* IRR 6 */
131     DISPATCH_LEVEL                 /* IRR 7 */
132 };
133 
134 #if defined(__GNUC__)
135 
136 #define HalpDelayedHardwareInterrupt(x)                             \
137     VOID __cdecl HalpHardwareInterrupt##x(VOID);                    \
138     VOID                                                            \
139     __cdecl                                                         \
140     HalpHardwareInterrupt##x(VOID)                                  \
141     {                                                               \
142         asm volatile ("int $%c0\n"::"i"(PRIMARY_VECTOR_BASE + x));  \
143     }
144 
145 #elif defined(_MSC_VER)
146 
147 #define HalpDelayedHardwareInterrupt(x)                             \
148     VOID __cdecl HalpHardwareInterrupt##x(VOID);                    \
149     VOID                                                            \
150     __cdecl                                                         \
151     HalpHardwareInterrupt##x(VOID)                                  \
152     {                                                               \
153         __asm                                                       \
154         {                                                           \
155             int PRIMARY_VECTOR_BASE + x                             \
156         }                                                           \
157     }
158 
159 #else
160 #error Unsupported compiler
161 #endif
162 
163 /* Pending/delayed hardware interrupt handlers */
164 HalpDelayedHardwareInterrupt(0);
165 HalpDelayedHardwareInterrupt(1);
166 HalpDelayedHardwareInterrupt(2);
167 HalpDelayedHardwareInterrupt(3);
168 HalpDelayedHardwareInterrupt(4);
169 HalpDelayedHardwareInterrupt(5);
170 HalpDelayedHardwareInterrupt(6);
171 HalpDelayedHardwareInterrupt(7);
172 HalpDelayedHardwareInterrupt(8);
173 HalpDelayedHardwareInterrupt(9);
174 HalpDelayedHardwareInterrupt(10);
175 HalpDelayedHardwareInterrupt(11);
176 HalpDelayedHardwareInterrupt(12);
177 HalpDelayedHardwareInterrupt(13);
178 HalpDelayedHardwareInterrupt(14);
179 HalpDelayedHardwareInterrupt(15);
180 
181 /* Handlers for pending interrupts */
182 PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[20] =
183 {
184     (PHAL_SW_INTERRUPT_HANDLER)KiUnexpectedInterrupt,
185     HalpApcInterrupt,
186     HalpDispatchInterrupt,
187     (PHAL_SW_INTERRUPT_HANDLER)KiUnexpectedInterrupt,
188     HalpHardwareInterrupt0,
189     HalpHardwareInterrupt1,
190     HalpHardwareInterrupt2,
191     HalpHardwareInterrupt3,
192     HalpHardwareInterrupt4,
193     HalpHardwareInterrupt5,
194     HalpHardwareInterrupt6,
195     HalpHardwareInterrupt7,
196     HalpHardwareInterrupt8,
197     HalpHardwareInterrupt9,
198     HalpHardwareInterrupt10,
199     HalpHardwareInterrupt11,
200     HalpHardwareInterrupt12,
201     HalpHardwareInterrupt13,
202     HalpHardwareInterrupt14,
203     HalpHardwareInterrupt15
204 };
205 
206 /* Handlers for pending software interrupts when we already have a trap frame*/
207 PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY SWInterruptHandlerTable2[3] =
208 {
209     (PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY)(PVOID)KiUnexpectedInterrupt,
210     HalpApcInterrupt2ndEntry,
211     HalpDispatchInterrupt2ndEntry
212 };
213 
214 LONG HalpEisaELCR;
215 
216 /* FUNCTIONS ******************************************************************/
217 
218 VOID
219 NTAPI
220 HalpInitializePICs(IN BOOLEAN EnableInterrupts)
221 {
222     ULONG EFlags;
223     EISA_ELCR Elcr;
224     ULONG i, j;
225     BOOLEAN ElcrFound;
226 
227     /* Save EFlags and disable interrupts */
228     EFlags = __readeflags();
229     _disable();
230 
231     /* Initialize and mask the PIC */
232     HalpInitializeLegacyPICs();
233 
234     /* Read EISA Edge/Level Register for master and slave */
235     Elcr.Bits = (__inbyte(EISA_ELCR_SLAVE) << 8) | __inbyte(EISA_ELCR_MASTER);
236 
237 #if defined(SARCH_PC98)
238     /* Force defaults when ELCR is not supported */
239     if (Elcr.Bits == 0xFFFF)
240     {
241         Elcr.Master.Irq0Level = 0;
242         Elcr.Master.Irq1Level = 0;
243         Elcr.Master.Irq7Level = 0;
244         Elcr.Slave.Irq8Level = 0;
245     }
246     ElcrFound = TRUE;
247 #else
248     /* IRQs 0, 1, 2, 8, and 13 are system-reserved and must be edge */
249     ElcrFound = (!(Elcr.Master.Irq0Level) && !(Elcr.Master.Irq1Level) && !(Elcr.Master.Irq2Level) &&
250                  !(Elcr.Slave.Irq8Level) && !(Elcr.Slave.Irq13Level));
251 #endif
252 
253     if (ElcrFound)
254     {
255         /* ELCR is as it's supposed to be, save it */
256         HalpEisaELCR = Elcr.Bits;
257 
258         /* Scan for level interrupts */
259         for (i = 1, j = 0; j < 16; i <<= 1, j++)
260         {
261             if (HalpEisaELCR & i)
262             {
263                 /* Switch handler to level */
264                 SWInterruptHandlerTable[j + 4] = HalpHardwareInterruptLevel;
265 
266                 /* Switch dismiss to level */
267                 HalpSpecialDismissTable[j] = HalpSpecialDismissLevelTable[j];
268             }
269         }
270     }
271 
272     /* Report cascade IRQ usage */
273     HalpRegisterVector(IDT_INTERNAL,
274                        PRIMARY_VECTOR_BASE + PIC_CASCADE_IRQ,
275                        PRIMARY_VECTOR_BASE + PIC_CASCADE_IRQ,
276                        HIGH_LEVEL);
277 
278     /* Restore interrupt state */
279     if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
280     __writeeflags(EFlags);
281 }
282 
283 UCHAR
284 FASTCALL
285 HalpIrqToVector(UCHAR Irq)
286 {
287     return (PRIMARY_VECTOR_BASE + Irq);
288 }
289 
290 UCHAR
291 FASTCALL
292 HalpVectorToIrq(UCHAR Vector)
293 {
294     return (Vector - PRIMARY_VECTOR_BASE);
295 }
296 
297 KIRQL
298 FASTCALL
299 HalpVectorToIrql(UCHAR Vector)
300 {
301     return (PROFILE_LEVEL - (Vector - PRIMARY_VECTOR_BASE));
302 }
303 
304 /* IRQL MANAGEMENT ************************************************************/
305 
306 /*
307  * @implemented
308  */
309 KIRQL
310 NTAPI
311 KeGetCurrentIrql(VOID)
312 {
313     /* Return the IRQL */
314     return KeGetPcr()->Irql;
315 }
316 
317 /*
318  * @implemented
319  */
320 KIRQL
321 NTAPI
322 KeRaiseIrqlToDpcLevel(VOID)
323 {
324     PKPCR Pcr = KeGetPcr();
325     KIRQL CurrentIrql;
326 
327     /* Save and update IRQL */
328     CurrentIrql = Pcr->Irql;
329     Pcr->Irql = DISPATCH_LEVEL;
330 
331 #if DBG
332     /* Validate correct raise */
333     if (CurrentIrql > DISPATCH_LEVEL) KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
334 #endif
335 
336     /* Return the previous value */
337     return CurrentIrql;
338 }
339 
340 /*
341  * @implemented
342  */
343 KIRQL
344 NTAPI
345 KeRaiseIrqlToSynchLevel(VOID)
346 {
347     PKPCR Pcr = KeGetPcr();
348     KIRQL CurrentIrql;
349 
350     /* Save and update IRQL */
351     CurrentIrql = Pcr->Irql;
352     Pcr->Irql = SYNCH_LEVEL;
353 
354 #if DBG
355     /* Validate correct raise */
356     if (CurrentIrql > SYNCH_LEVEL)
357     {
358         /* Crash system */
359         KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
360                      CurrentIrql,
361                      SYNCH_LEVEL,
362                      0,
363                      1);
364     }
365 #endif
366 
367     /* Return the previous value */
368     return CurrentIrql;
369 }
370 
371 /*
372  * @implemented
373  */
374 KIRQL
375 FASTCALL
376 KfRaiseIrql(IN KIRQL NewIrql)
377 {
378     PKPCR Pcr = KeGetPcr();
379     KIRQL CurrentIrql;
380 
381     /* Read current IRQL */
382     CurrentIrql = Pcr->Irql;
383 
384 #if DBG
385     /* Validate correct raise */
386     if (CurrentIrql > NewIrql)
387     {
388         /* Crash system */
389         Pcr->Irql = PASSIVE_LEVEL;
390         KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
391     }
392 #endif
393 
394     /* Set new IRQL */
395     Pcr->Irql = NewIrql;
396 
397     /* Return old IRQL */
398     return CurrentIrql;
399 }
400 
401 
402 /*
403  * @implemented
404  */
405 VOID
406 FASTCALL
407 KfLowerIrql(IN KIRQL OldIrql)
408 {
409     ULONG EFlags;
410     ULONG PendingIrql, PendingIrqlMask;
411     PKPCR Pcr = KeGetPcr();
412     PIC_MASK Mask;
413 
414 #if DBG
415     /* Validate correct lower */
416     if (OldIrql > Pcr->Irql)
417     {
418         /* Crash system */
419         Pcr->Irql = HIGH_LEVEL;
420         KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
421     }
422 #endif
423 
424     /* Save EFlags and disable interrupts */
425     EFlags = __readeflags();
426     _disable();
427 
428     /* Set old IRQL */
429     Pcr->Irql = OldIrql;
430 
431     /* Check for pending software interrupts and compare with current IRQL */
432     PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
433     if (PendingIrqlMask)
434     {
435         /* Check if pending IRQL affects hardware state */
436         BitScanReverse(&PendingIrql, PendingIrqlMask);
437         if (PendingIrql > DISPATCH_LEVEL)
438         {
439             /* Set new PIC mask */
440             Mask.Both = Pcr->IDR & 0xFFFF;
441             __outbyte(PIC1_DATA_PORT, Mask.Master);
442             __outbyte(PIC2_DATA_PORT, Mask.Slave);
443 
444             /* Clear IRR bit */
445             Pcr->IRR ^= (1 << PendingIrql);
446         }
447 
448         /* Now handle pending interrupt */
449         SWInterruptHandlerTable[PendingIrql]();
450     }
451 
452     /* Restore interrupt state */
453     __writeeflags(EFlags);
454 }
455 
456 /* SOFTWARE INTERRUPTS ********************************************************/
457 
458 /*
459  * @implemented
460  */
461 VOID
462 FASTCALL
463 HalRequestSoftwareInterrupt(IN KIRQL Irql)
464 {
465     ULONG EFlags;
466     PKPCR Pcr = KeGetPcr();
467     KIRQL PendingIrql;
468 
469     /* Save EFlags and disable interrupts */
470     EFlags = __readeflags();
471     _disable();
472 
473     /* Mask out the requested bit */
474     Pcr->IRR |= (1 << Irql);
475 
476     /* Check for pending software interrupts and compare with current IRQL */
477     PendingIrql = SWInterruptLookUpTable[Pcr->IRR & 3];
478     if (PendingIrql > Pcr->Irql) SWInterruptHandlerTable[PendingIrql]();
479 
480     /* Restore interrupt state */
481     __writeeflags(EFlags);
482 }
483 
484 /*
485  * @implemented
486  */
487 VOID
488 FASTCALL
489 HalClearSoftwareInterrupt(IN KIRQL Irql)
490 {
491     /* Mask out the requested bit */
492     KeGetPcr()->IRR &= ~(1 << Irql);
493 }
494 
495 PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY
496 FASTCALL
497 HalpEndSoftwareInterrupt2(IN KIRQL OldIrql,
498                           IN PKTRAP_FRAME TrapFrame)
499 {
500     ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
501     PKPCR Pcr = KeGetPcr();
502     PIC_MASK Mask;
503 
504     UNREFERENCED_PARAMETER(TrapFrame);
505 
506     /* Set old IRQL */
507     Pcr->Irql = OldIrql;
508 
509     /* Loop checking for pending interrupts */
510     while (TRUE)
511     {
512         /* Check for pending software interrupts and compare with current IRQL */
513         PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
514         if (!PendingIrqlMask) return NULL;
515 
516         /* Check for in-service delayed interrupt */
517         if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
518 
519         /* Check if pending IRQL affects hardware state */
520         BitScanReverse(&PendingIrql, PendingIrqlMask);
521         if (PendingIrql > DISPATCH_LEVEL)
522         {
523             /* Set new PIC mask */
524             Mask.Both = Pcr->IDR & 0xFFFF;
525             __outbyte(PIC1_DATA_PORT, Mask.Master);
526             __outbyte(PIC2_DATA_PORT, Mask.Slave);
527 
528             /* Set active bit otherwise, and clear it from IRR */
529             PendingIrqMask = (1 << PendingIrql);
530             Pcr->IrrActive |= PendingIrqMask;
531             Pcr->IRR ^= PendingIrqMask;
532 
533             /* Handle delayed hardware interrupt */
534             SWInterruptHandlerTable[PendingIrql]();
535 
536             /* Handling complete */
537             Pcr->IrrActive ^= PendingIrqMask;
538         }
539         else
540         {
541             /* No need to loop checking for hardware interrupts */
542             return SWInterruptHandlerTable2[PendingIrql];
543         }
544     }
545 
546     return NULL;
547 }
548 
549 /* EDGE INTERRUPT DISMISSAL FUNCTIONS *****************************************/
550 
551 FORCEINLINE
552 BOOLEAN
553 _HalpDismissIrqGeneric(IN KIRQL Irql,
554                        IN ULONG Irq,
555                        OUT PKIRQL OldIrql)
556 {
557     PIC_MASK Mask;
558     KIRQL CurrentIrql;
559     I8259_OCW2 Ocw2;
560     PKPCR Pcr = KeGetPcr();
561 
562     /* First save current IRQL and compare it to the requested one */
563     CurrentIrql = Pcr->Irql;
564 
565     /* Check if this interrupt is really allowed to happen */
566     if (Irql > CurrentIrql)
567     {
568         /* Set the new IRQL and return the current one */
569         Pcr->Irql = Irql;
570         *OldIrql = CurrentIrql;
571 
572         /* Prepare OCW2 for EOI */
573         Ocw2.Bits = 0;
574         Ocw2.EoiMode = SpecificEoi;
575 
576         /* Check which PIC needs the EOI */
577         if (Irq >= 8)
578         {
579 #if defined(SARCH_PC98)
580             I8259_OCW3 Ocw3;
581             I8259_ISR Isr;
582 
583             /* Send the EOI for the IRQ */
584             __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
585 
586             /* Request the ISR */
587             Ocw3.Bits = 0;
588             Ocw3.Sbo = 1;
589             Ocw3.ReadRequest = ReadIsr;
590             __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
591 
592             /* Read the ISR */
593             Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
594 
595             /* Check if the interrupt serviced was the only one from the slave PIC */
596             if (Isr.Bits == 0)
597             {
598                 /* If ISR is empty, send the EOI for cascade IRQ on the master PIC */
599                 __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
600             }
601 #else
602             /* Send the EOI for the IRQ */
603             __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
604 
605             /* Send the EOI for cascade IRQ on the master PIC */
606             __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
607 #endif
608         }
609         else
610         {
611             /* Send the EOI for the IRQ */
612             __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq & 0xFF));
613         }
614 
615         /* Enable interrupts and return success */
616         _enable();
617         return TRUE;
618     }
619 
620     /* Update the IRR so that we deliver this interrupt when the IRQL is proper */
621     Pcr->IRR |= (1 << (Irq + 4));
622 
623     /* Set new PIC mask to real IRQL level, since the optimization is lost now */
624     Mask.Both = (KiI8259MaskTable[CurrentIrql] | Pcr->IDR) & 0xFFFF;
625     __outbyte(PIC1_DATA_PORT, Mask.Master);
626     __outbyte(PIC2_DATA_PORT, Mask.Slave);
627 
628     /* Now lie and say this was spurious */
629     return FALSE;
630 }
631 
632 BOOLEAN
633 NTAPI
634 HalpDismissIrqGeneric(IN KIRQL Irql,
635                       IN ULONG Irq,
636                       OUT PKIRQL OldIrql)
637 {
638     /* Run the inline code */
639     return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
640 }
641 
642 BOOLEAN
643 NTAPI
644 HalpDismissIrq15(IN KIRQL Irql,
645                  IN ULONG Irq,
646                  OUT PKIRQL OldIrql)
647 {
648     I8259_OCW3 Ocw3;
649     I8259_OCW2 Ocw2;
650     I8259_ISR Isr;
651 
652     /* Request the ISR */
653     Ocw3.Bits = 0;
654     Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
655     Ocw3.ReadRequest = ReadIsr;
656     __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
657 
658     /* Read the ISR */
659     Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
660 
661     /* Is IRQ15 really active (this is IR7) */
662     if (Isr.Irq7 == FALSE)
663     {
664         /* It isn't, so we have to EOI cascade IRQ */
665         Ocw2.Bits = 0;
666         Ocw2.EoiMode = SpecificEoi;
667         __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
668 
669         /* And now fail since this was spurious */
670         return FALSE;
671     }
672 
673     /* Do normal interrupt dismiss */
674     return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
675 }
676 
677 BOOLEAN
678 NTAPI
679 HalpDismissIrq13(IN KIRQL Irql,
680                  IN ULONG Irq,
681                  OUT PKIRQL OldIrql)
682 {
683     /* Clear the FPU busy latch */
684     __outbyte(0xF0, 0);
685 
686     /* Do normal interrupt dismiss */
687     return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
688 }
689 
690 #if defined(SARCH_PC98)
691 BOOLEAN
692 NTAPI
693 HalpDismissIrq08(
694     _In_ KIRQL Irql,
695     _In_ ULONG Irq,
696     _Out_ PKIRQL OldIrql)
697 {
698     /* Clear the FPU busy latch */
699     __outbyte(CPU_IO_o_FPU_BUSY_LATCH, 0);
700 
701     /* Do normal interrupt dismiss */
702     return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
703 }
704 #endif
705 
706 BOOLEAN
707 NTAPI
708 HalpDismissIrq07(IN KIRQL Irql,
709                  IN ULONG Irq,
710                  OUT PKIRQL OldIrql)
711 {
712     I8259_OCW3 Ocw3;
713     I8259_ISR Isr;
714 
715     /* Request the ISR */
716     Ocw3.Bits = 0;
717     Ocw3.Sbo = 1;
718     Ocw3.ReadRequest = ReadIsr;
719     __outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);
720 
721     /* Read the ISR */
722     Isr.Bits = __inbyte(PIC1_CONTROL_PORT);
723 
724     /* Is IRQ 7 really active? If it isn't, this is spurious so fail */
725     if (Isr.Irq7 == FALSE) return FALSE;
726 
727     /* Do normal interrupt dismiss */
728     return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
729 }
730 
731 /* LEVEL INTERRUPT DISMISSAL FUNCTIONS ****************************************/
732 
733 FORCEINLINE
734 BOOLEAN
735 _HalpDismissIrqLevel(IN KIRQL Irql,
736                      IN ULONG Irq,
737                      OUT PKIRQL OldIrql)
738 {
739     PIC_MASK Mask;
740     KIRQL CurrentIrql;
741     I8259_OCW2 Ocw2;
742     PKPCR Pcr = KeGetPcr();
743 
744     /* Update the PIC */
745     Mask.Both = (KiI8259MaskTable[Irql] | Pcr->IDR) & 0xFFFF;
746     __outbyte(PIC1_DATA_PORT, Mask.Master);
747     __outbyte(PIC2_DATA_PORT, Mask.Slave);
748 
749     /* Update the IRR so that we clear this interrupt when the IRQL is proper */
750     Pcr->IRR |= (1 << (Irq + 4));
751 
752     /* Save current IRQL */
753     CurrentIrql = Pcr->Irql;
754 
755     /* Prepare OCW2 for EOI */
756     Ocw2.Bits = 0;
757     Ocw2.EoiMode = SpecificEoi;
758 
759     /* Check which PIC needs the EOI */
760     if (Irq >= 8)
761     {
762 #if defined(SARCH_PC98)
763         I8259_OCW3 Ocw3;
764         I8259_ISR Isr;
765 
766         /* Send the EOI for the IRQ */
767         __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
768 
769         /* Request the ISR */
770         Ocw3.Bits = 0;
771         Ocw3.Sbo = 1;
772         Ocw3.ReadRequest = ReadIsr;
773         __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
774 
775         /* Read the ISR */
776         Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
777 
778         /* Check if the interrupt serviced was the only one from the slave PIC */
779         if (Isr.Bits == 0)
780         {
781             /* If ISR is empty, send the EOI for cascade IRQ on the master PIC */
782             __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
783         }
784 #else
785         /* Send the EOI for the IRQ */
786         __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));
787 
788         /* Send the EOI for cascade IRQ on the master PIC */
789         __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
790 #endif
791     }
792     else
793     {
794         /* Send the EOI for the IRQ */
795         __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq & 0xFF));
796     }
797 
798     /* Check if this interrupt should be allowed to happen */
799     if (Irql > CurrentIrql)
800     {
801         /* Set the new IRQL and return the current one */
802         Pcr->Irql = Irql;
803         *OldIrql = CurrentIrql;
804 
805         /* Enable interrupts and return success */
806         _enable();
807         return TRUE;
808     }
809 
810     /* Now lie and say this was spurious */
811     return FALSE;
812 }
813 
814 BOOLEAN
815 NTAPI
816 HalpDismissIrqLevel(IN KIRQL Irql,
817                     IN ULONG Irq,
818                     OUT PKIRQL OldIrql)
819 {
820     /* Run the inline code */
821     return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
822 }
823 
824 BOOLEAN
825 NTAPI
826 HalpDismissIrq15Level(IN KIRQL Irql,
827                       IN ULONG Irq,
828                       OUT PKIRQL OldIrql)
829 {
830     I8259_OCW3 Ocw3;
831     I8259_OCW2 Ocw2;
832     I8259_ISR Isr;
833 
834     /* Request the ISR */
835     Ocw3.Bits = 0;
836     Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
837     Ocw3.ReadRequest = ReadIsr;
838     __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);
839 
840     /* Read the ISR */
841     Isr.Bits = __inbyte(PIC2_CONTROL_PORT);
842 
843     /* Is IRQ15 really active (this is IR7) */
844     if (Isr.Irq7 == FALSE)
845     {
846         /* It isn't, so we have to EOI cascade IRQ */
847         Ocw2.Bits = 0;
848         Ocw2.EoiMode = SpecificEoi;
849         __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | PIC_CASCADE_IRQ);
850 
851         /* And now fail since this was spurious */
852         return FALSE;
853     }
854 
855     /* Do normal interrupt dismiss */
856     return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
857 }
858 
859 BOOLEAN
860 NTAPI
861 HalpDismissIrq13Level(IN KIRQL Irql,
862                       IN ULONG Irq,
863                       OUT PKIRQL OldIrql)
864 {
865     /* Clear the FPU busy latch */
866     __outbyte(0xF0, 0);
867 
868     /* Do normal interrupt dismiss */
869     return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
870 }
871 
872 #if defined(SARCH_PC98)
873 BOOLEAN
874 NTAPI
875 HalpDismissIrq08Level(
876     _In_ KIRQL Irql,
877     _In_ ULONG Irq,
878     _Out_ PKIRQL OldIrql)
879 {
880     /* Clear the FPU busy latch */
881     __outbyte(CPU_IO_o_FPU_BUSY_LATCH, 0);
882 
883     /* Do normal interrupt dismiss */
884     return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
885 }
886 #endif
887 
888 BOOLEAN
889 NTAPI
890 HalpDismissIrq07Level(IN KIRQL Irql,
891                       IN ULONG Irq,
892                       OUT PKIRQL OldIrql)
893 {
894     I8259_OCW3 Ocw3;
895     I8259_ISR Isr;
896 
897     /* Request the ISR */
898     Ocw3.Bits = 0;
899     Ocw3.Sbo = 1;
900     Ocw3.ReadRequest = ReadIsr;
901     __outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);
902 
903     /* Read the ISR */
904     Isr.Bits = __inbyte(PIC1_CONTROL_PORT);
905 
906     /* Is IRQ 7 really active? If it isn't, this is spurious so fail */
907     if (Isr.Irq7 == FALSE) return FALSE;
908 
909     /* Do normal interrupt dismiss */
910     return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
911 }
912 
913 PHAL_SW_INTERRUPT_HANDLER
914 __cdecl
915 HalpHardwareInterruptLevel2(VOID)
916 {
917     PKPCR Pcr = KeGetPcr();
918     ULONG PendingIrqlMask, PendingIrql;
919 
920     /* Check for pending software interrupts and compare with current IRQL */
921     PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
922     if (PendingIrqlMask)
923     {
924         /* Check for in-service delayed interrupt */
925         if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
926 
927         /* Check if pending IRQL affects hardware state */
928         BitScanReverse(&PendingIrql, PendingIrqlMask);
929 
930         /* Clear IRR bit */
931         Pcr->IRR ^= (1 << PendingIrql);
932 
933         /* Now handle pending interrupt */
934         return SWInterruptHandlerTable[PendingIrql];
935     }
936 
937     return NULL;
938 }
939 
940 /* SYSTEM INTERRUPTS **********************************************************/
941 
942 /*
943  * @implemented
944  */
945 BOOLEAN
946 NTAPI
947 HalEnableSystemInterrupt(IN ULONG Vector,
948                          IN KIRQL Irql,
949                          IN KINTERRUPT_MODE InterruptMode)
950 {
951     ULONG Irq;
952     PKPCR Pcr = KeGetPcr();
953     PIC_MASK PicMask;
954 
955     /* Validate the IRQ */
956     Irq = Vector - PRIMARY_VECTOR_BASE;
957     if (Irq >= CLOCK2_LEVEL) return FALSE;
958 
959     /* Check for level interrupt */
960     if (InterruptMode == LevelSensitive)
961     {
962         /* Switch handler to level */
963         SWInterruptHandlerTable[Irq + 4] = HalpHardwareInterruptLevel;
964 
965         /* Switch dismiss to level */
966         HalpSpecialDismissTable[Irq] = HalpSpecialDismissLevelTable[Irq];
967     }
968 
969     /* Disable interrupts */
970     _disable();
971 
972     /* Update software IDR */
973     Pcr->IDR &= ~(1 << Irq);
974 
975     /* Set new PIC mask */
976     PicMask.Both = (KiI8259MaskTable[Pcr->Irql] | Pcr->IDR) & 0xFFFF;
977     __outbyte(PIC1_DATA_PORT, PicMask.Master);
978     __outbyte(PIC2_DATA_PORT, PicMask.Slave);
979 
980     /* Enable interrupts and exit */
981     _enable();
982     return TRUE;
983 }
984 
985 /*
986  * @implemented
987  */
988 VOID
989 NTAPI
990 HalDisableSystemInterrupt(IN ULONG Vector,
991                           IN KIRQL Irql)
992 {
993     ULONG IrqMask;
994     PIC_MASK PicMask;
995 
996     /* Compute new combined IRQ mask */
997     IrqMask = 1 << (Vector - PRIMARY_VECTOR_BASE);
998 
999     /* Disable interrupts */
1000     _disable();
1001 
1002     /* Update software IDR */
1003     KeGetPcr()->IDR |= IrqMask;
1004 
1005     /* Read current interrupt mask */
1006     PicMask.Master = __inbyte(PIC1_DATA_PORT);
1007     PicMask.Slave = __inbyte(PIC2_DATA_PORT);
1008 
1009     /* Add the new disabled interrupt */
1010     PicMask.Both |= IrqMask;
1011 
1012     /* Write new interrupt mask */
1013     __outbyte(PIC1_DATA_PORT, PicMask.Master);
1014     __outbyte(PIC2_DATA_PORT, PicMask.Slave);
1015 
1016     /* Bring interrupts back */
1017     _enable();
1018 }
1019 
1020 /*
1021  * @implemented
1022  */
1023 BOOLEAN
1024 NTAPI
1025 HalBeginSystemInterrupt(IN KIRQL Irql,
1026                         IN ULONG Vector,
1027                         OUT PKIRQL OldIrql)
1028 {
1029     ULONG Irq;
1030 
1031     /* Get the IRQ and call the proper routine to handle it */
1032     Irq = Vector - PRIMARY_VECTOR_BASE;
1033     return HalpSpecialDismissTable[Irq](Irql, Irq, OldIrql);
1034 }
1035 
1036 /*
1037  * @implemented
1038  */
1039 PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY
1040 FASTCALL
1041 HalEndSystemInterrupt2(IN KIRQL OldIrql,
1042                        IN PKTRAP_FRAME TrapFrame)
1043 {
1044     ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
1045     PKPCR Pcr = KeGetPcr();
1046     PIC_MASK Mask;
1047 
1048     /* Set old IRQL */
1049     Pcr->Irql = OldIrql;
1050 
1051     /* Check for pending software interrupts and compare with current IRQL */
1052     PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
1053     if (PendingIrqlMask)
1054     {
1055         /* Check for in-service delayed interrupt */
1056         if (Pcr->IrrActive & 0xFFFFFFF0) return NULL;
1057 
1058         /* Loop checking for pending interrupts */
1059         while (TRUE)
1060         {
1061             /* Check if pending IRQL affects hardware state */
1062             BitScanReverse(&PendingIrql, PendingIrqlMask);
1063             if (PendingIrql > DISPATCH_LEVEL)
1064             {
1065                 /* Set new PIC mask */
1066                 Mask.Both = Pcr->IDR & 0xFFFF;
1067                 __outbyte(PIC1_DATA_PORT, Mask.Master);
1068                 __outbyte(PIC2_DATA_PORT, Mask.Slave);
1069 
1070                 /* Now check if this specific interrupt is already in-service */
1071                 PendingIrqMask = (1 << PendingIrql);
1072                 if (Pcr->IrrActive & PendingIrqMask) return NULL;
1073 
1074                 /* Set active bit otherwise, and clear it from IRR */
1075                 Pcr->IrrActive |= PendingIrqMask;
1076                 Pcr->IRR ^= PendingIrqMask;
1077 
1078                 /* Handle delayed hardware interrupt */
1079                 SWInterruptHandlerTable[PendingIrql]();
1080 
1081                 /* Handling complete */
1082                 Pcr->IrrActive ^= PendingIrqMask;
1083 
1084                 /* Check if there's still interrupts pending */
1085                 PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
1086                 if (!PendingIrqlMask) break;
1087             }
1088             else
1089             {
1090                 /* Now handle pending software interrupt */
1091                 return SWInterruptHandlerTable2[PendingIrql];
1092             }
1093         }
1094     }
1095 
1096     return NULL;
1097 }
1098 
1099 /* SOFTWARE INTERRUPT TRAPS ***************************************************/
1100 
1101 FORCEINLINE
1102 DECLSPEC_NORETURN
1103 VOID
1104 _HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
1105 {
1106     KIRQL CurrentIrql;
1107     PKPCR Pcr = KeGetPcr();
1108 
1109     /* Save the current IRQL and update it */
1110     CurrentIrql = Pcr->Irql;
1111     Pcr->Irql = APC_LEVEL;
1112 
1113     /* Remove DPC from IRR */
1114     Pcr->IRR &= ~(1 << APC_LEVEL);
1115 
1116     /* Enable interrupts and call the kernel's APC interrupt handler */
1117     _enable();
1118     KiDeliverApc(((KiUserTrap(TrapFrame)) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) ?
1119                 UserMode : KernelMode,
1120                 NULL,
1121                 TrapFrame);
1122 
1123     /* Disable interrupts and end the interrupt */
1124     _disable();
1125     HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);
1126 
1127     /* Exit the interrupt */
1128     KiEoiHelper(TrapFrame);
1129 }
1130 
1131 DECLSPEC_NORETURN
1132 VOID
1133 FASTCALL
1134 HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
1135 {
1136     /* Do the work */
1137     _HalpApcInterruptHandler(TrapFrame);
1138 }
1139 
1140 DECLSPEC_NORETURN
1141 VOID
1142 FASTCALL
1143 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
1144 {
1145     /* Set up a fake INT Stack */
1146     TrapFrame->EFlags = __readeflags();
1147     TrapFrame->SegCs = KGDT_R0_CODE;
1148     TrapFrame->Eip = TrapFrame->Eax;
1149 
1150     /* Build the trap frame */
1151     KiEnterInterruptTrap(TrapFrame);
1152 
1153     /* Do the work */
1154     _HalpApcInterruptHandler(TrapFrame);
1155 }
1156 
1157 FORCEINLINE
1158 KIRQL
1159 _HalpDispatchInterruptHandler(VOID)
1160 {
1161     KIRQL CurrentIrql;
1162     PKPCR Pcr = KeGetPcr();
1163 
1164     /* Save the current IRQL and update it */
1165     CurrentIrql = Pcr->Irql;
1166     Pcr->Irql = DISPATCH_LEVEL;
1167 
1168     /* Remove DPC from IRR */
1169     Pcr->IRR &= ~(1 << DISPATCH_LEVEL);
1170 
1171     /* Enable interrupts and call the kernel's DPC interrupt handler */
1172     _enable();
1173     KiDispatchInterrupt();
1174     _disable();
1175 
1176     /* Return IRQL */
1177     return CurrentIrql;
1178 }
1179 
1180 DECLSPEC_NORETURN
1181 VOID
1182 FASTCALL
1183 HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
1184 {
1185     KIRQL CurrentIrql;
1186 
1187     /* Do the work */
1188     CurrentIrql = _HalpDispatchInterruptHandler();
1189 
1190     /* End the interrupt */
1191     HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);
1192 
1193     /* Exit the interrupt */
1194     KiEoiHelper(TrapFrame);
1195 }
1196 
1197 PHAL_SW_INTERRUPT_HANDLER
1198 __cdecl
1199 HalpDispatchInterrupt2(VOID)
1200 {
1201     ULONG PendingIrqlMask, PendingIrql;
1202     KIRQL OldIrql;
1203     PIC_MASK Mask;
1204     PKPCR Pcr = KeGetPcr();
1205 
1206     /* Do the work */
1207     OldIrql = _HalpDispatchInterruptHandler();
1208 
1209     /* Restore IRQL */
1210     Pcr->Irql = OldIrql;
1211 
1212     /* Check for pending software interrupts and compare with current IRQL */
1213     PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
1214     if (PendingIrqlMask)
1215     {
1216         /* Check if pending IRQL affects hardware state */
1217         BitScanReverse(&PendingIrql, PendingIrqlMask);
1218         if (PendingIrql > DISPATCH_LEVEL)
1219         {
1220             /* Set new PIC mask */
1221             Mask.Both = Pcr->IDR & 0xFFFF;
1222             __outbyte(PIC1_DATA_PORT, Mask.Master);
1223             __outbyte(PIC2_DATA_PORT, Mask.Slave);
1224 
1225             /* Clear IRR bit */
1226             Pcr->IRR ^= (1 << PendingIrql);
1227         }
1228 
1229         /* Now handle pending interrupt */
1230         return SWInterruptHandlerTable[PendingIrql];
1231     }
1232 
1233     return NULL;
1234 }
1235 
1236 ULONG
1237 NTAPI
1238 HalpGetRootInterruptVector(IN ULONG BusInterruptLevel,
1239                            IN ULONG BusInterruptVector,
1240                            OUT PKIRQL Irql,
1241                            OUT PKAFFINITY Affinity)
1242 {
1243     UCHAR SystemVector;
1244 
1245     /* Validate the IRQ */
1246     if (BusInterruptLevel > 23)
1247     {
1248         /* Invalid vector */
1249         DPRINT1("IRQ %lx is too high!\n", BusInterruptLevel);
1250         return 0;
1251     }
1252 
1253     /* Get the system vector */
1254     SystemVector = HalpIrqToVector((UCHAR)BusInterruptLevel);
1255 
1256     /* Return the IRQL and affinity */
1257     *Irql = HalpVectorToIrql(SystemVector);
1258     *Affinity = HalpDefaultInterruptAffinity;
1259     ASSERT(HalpDefaultInterruptAffinity);
1260 
1261     /* Return the vector */
1262     return SystemVector;
1263 }
1264 
1265 #else /* _MINIHAL_ */
1266 
1267 KIRQL
1268 NTAPI
1269 KeGetCurrentIrql(VOID)
1270 {
1271     return PASSIVE_LEVEL;
1272 }
1273 
1274 VOID
1275 FASTCALL
1276 KfLowerIrql(
1277     IN KIRQL OldIrql)
1278 {
1279 }
1280 
1281 KIRQL
1282 FASTCALL
1283 KfRaiseIrql(
1284     IN KIRQL NewIrql)
1285 {
1286     return NewIrql;
1287 }
1288 
1289 #endif /* !_MINIHAL_ */
1290