[PERF]: Optimize nested interrupt cases (where a pending interrupt exists at the...
[reactos.git] / reactos / hal / halx86 / generic / pic.c
index 06e098b..e2f08c4 100644 (file)
@@ -184,6 +184,36 @@ ULONG KiI8259MaskTable[32] =
 #endif
 };
 
+/* Denotes minimum required IRQL before we can process pending SW interrupts */
+KIRQL SWInterruptLookUpTable[8] =
+{
+    PASSIVE_LEVEL,                 /* IRR 0 */
+    PASSIVE_LEVEL,                 /* IRR 1 */
+    APC_LEVEL,                     /* IRR 2 */
+    APC_LEVEL,                     /* IRR 3 */
+    DISPATCH_LEVEL,                /* IRR 4 */
+    DISPATCH_LEVEL,                /* IRR 5 */
+    DISPATCH_LEVEL,                /* IRR 6 */
+    DISPATCH_LEVEL                 /* IRR 7 */
+};
+
+/* Handlers for pending software interrupts */
+PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[3] =
+{
+    KiUnexpectedInterrupt,
+    HalpApcInterrupt,
+    HalpDispatchInterrupt
+};
+
+/* Handlers for pending software interrupts when we already have a trap frame*/
+PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY SWInterruptHandlerTable2[3] =
+{
+    (PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY)KiUnexpectedInterrupt,
+    HalpApcInterrupt2ndEntry,
+    HalpDispatchInterrupt2ndEntry
+};
+
+
 USHORT HalpEisaELCR;
 
 /* FUNCTIONS ******************************************************************/
@@ -418,8 +448,86 @@ KfRaiseIrql(IN KIRQL NewIrql)
     return CurrentIrql;
 }
 
+
+/*
+ * @implemented
+ */
+VOID
+FASTCALL
+KfLowerIrql(IN KIRQL OldIrql)
+{
+    ULONG EFlags;
+    KIRQL PendingIrql;
+    PKPCR Pcr = KeGetPcr();
+    PIC_MASK Mask;
+    
+#ifdef IRQL_DEBUG
+    /* Validate correct lower */
+    if (OldIrql > Pcr->Irql)
+    {
+        /* Crash system */
+        KIRQL CurrentIrql = Pcr->Irql;
+        Pcr->Irql = HIGH_LEVEL;
+        KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL,
+                     CurrentIrql,
+                     OldIrql,
+                     0,
+                     3);
+    }
+#endif
+    
+    /* Save EFlags and disable interrupts */
+    EFlags = __readeflags();
+    _disable();
+
+    /* Check if currentl IRQL affects hardware state */
+    if (Pcr->Irql > DISPATCH_LEVEL)
+    {        
+        /* Set new PIC mask */
+        Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
+        __outbyte(PIC1_DATA_PORT, Mask.Master);
+        __outbyte(PIC2_DATA_PORT, Mask.Slave);
+    }
+
+    /* Set old IRQL */
+    Pcr->Irql = OldIrql;
+    
+    /* Check for pending software interrupts and compare with current IRQL */
+    PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
+    if (PendingIrql > OldIrql) SWInterruptHandlerTable[PendingIrql]();
+
+    /* Restore interrupt state */
+    __writeeflags(EFlags);
+}
+
 /* SOFTWARE INTERRUPTS ********************************************************/
 
+/*
+ * @implemented
+ */
+VOID
+FASTCALL
+HalRequestSoftwareInterrupt(IN KIRQL Irql)
+{
+    ULONG EFlags;
+    PKPCR Pcr = KeGetPcr();
+    KIRQL PendingIrql;
+    
+    /* Save EFlags and disable interrupts */
+    EFlags = __readeflags();
+    _disable();
+    
+    /* Mask out the requested bit */
+    Pcr->IRR |= (1 << Irql);
+
+    /* Check for pending software interrupts and compare with current IRQL */
+    PendingIrql = SWInterruptLookUpTable[Pcr->IRR & 3];
+    if (PendingIrql > Pcr->Irql) SWInterruptHandlerTable[PendingIrql]();
+    
+    /* Restore interrupt state */
+    __writeeflags(EFlags);
+}
+
 /*
  * @implemented
  */
@@ -431,6 +539,31 @@ HalClearSoftwareInterrupt(IN KIRQL Irql)
     KeGetPcr()->IRR &= ~(1 << Irql);
 }
 
+VOID
+NTAPI
+HalpEndSoftwareInterrupt(IN KIRQL OldIrql)
+{
+    KIRQL PendingIrql;
+    PKPCR Pcr = KeGetPcr();
+    PIC_MASK Mask;
+
+    /* Check if currentl IRQL affects hardware state */
+    if (Pcr->Irql > DISPATCH_LEVEL)
+    {        
+        /* Set new PIC mask */
+        Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
+        __outbyte(PIC1_DATA_PORT, Mask.Master);
+        __outbyte(PIC2_DATA_PORT, Mask.Slave);
+    }
+
+    /* Set old IRQL */
+    Pcr->Irql = OldIrql;
+    
+    /* Check for pending software interrupts and compare with current IRQL */
+    PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
+    if (PendingIrql > OldIrql) HalpNestedTrap(PendingIrql);
+}
+
 /* INTERRUPT DISMISSAL FUNCTIONS **********************************************/
 
 BOOLEAN
@@ -658,3 +791,148 @@ HalBeginSystemInterrupt(IN KIRQL Irql,
     Irq = Vector - PRIMARY_VECTOR_BASE;
     return HalpSpecialDismissTable[Irq](Irql, Irq, OldIrql);
 }
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+HalEndSystemInterrupt(IN KIRQL OldIrql,
+                      IN UCHAR Vector)
+{
+    KIRQL PendingIrql;
+    PKPCR Pcr = KeGetPcr();
+    PIC_MASK Mask;
+
+    /* Check if currentl IRQL affects hardware state */
+    if (Pcr->Irql > DISPATCH_LEVEL)
+    {        
+        /* Set new PIC mask */
+        Mask.Both = KiI8259MaskTable[OldIrql] | Pcr->IDR;
+        __outbyte(PIC1_DATA_PORT, Mask.Master);
+        __outbyte(PIC2_DATA_PORT, Mask.Slave);
+    }
+
+    /* Set old IRQL */
+    Pcr->Irql = OldIrql;
+    
+    /* Check for pending software interrupts and compare with current IRQL */
+    PendingIrql = SWInterruptLookUpTable[Pcr->IRR];
+    if (PendingIrql > OldIrql) HalpNestedTrap(PendingIrql);
+}
+
+/* SOFTWARE INTERRUPT TRAPS ***************************************************/
+
+VOID
+FORCEINLINE
+DECLSPEC_NORETURN
+_HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
+{
+    KIRQL CurrentIrql;
+    PKPCR Pcr = KeGetPcr();
+    
+    /* Save the current IRQL and update it */
+    CurrentIrql = Pcr->Irql;
+    Pcr->Irql = APC_LEVEL;
+
+    /* Remove DPC from IRR */
+    Pcr->IRR &= ~(1 << APC_LEVEL);
+
+    /* Enable interrupts and call the kernel's APC interrupt handler */
+    _enable();
+    KiDeliverApc(((KiUserTrap(TrapFrame)) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) ?
+                UserMode : KernelMode,
+                NULL,
+                TrapFrame);
+
+    /* Disable interrupts and end the interrupt */
+    _disable();
+    Pcr->VdmAlert = (ULONG_PTR)TrapFrame;
+    HalpEndSoftwareInterrupt(CurrentIrql);
+
+    /* Exit the interrupt */
+    KiEoiHelper(TrapFrame); 
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
+{
+    /* Do the work */
+    _HalpApcInterruptHandler(TrapFrame);
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
+{
+    /* Set up a fake INT Stack */
+    TrapFrame->EFlags = __readeflags();
+    TrapFrame->SegCs = KGDT_R0_CODE;
+    TrapFrame->Eip = TrapFrame->Eax;
+    
+    /* Build the trap frame */
+    KiEnterInterruptTrap(TrapFrame);
+    
+    /* Do the work */
+    _HalpApcInterruptHandler(TrapFrame);
+}
+
+VOID
+FORCEINLINE
+DECLSPEC_NORETURN
+_HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
+{
+    KIRQL CurrentIrql;
+    PKPCR Pcr = KeGetPcr();
+    
+    /* Save the current IRQL and update it */
+    CurrentIrql = Pcr->Irql;
+    Pcr->Irql = DISPATCH_LEVEL;
+    
+    /* Remove DPC from IRR */
+    Pcr->IRR &= ~(1 << DISPATCH_LEVEL);
+    
+    /* Enable interrupts and call the kernel's DPC interrupt handler */
+    _enable();
+    KiDispatchInterrupt();
+    
+    /* Disable interrupts and end the interrupt */
+    _disable();
+    Pcr->VdmAlert = (ULONG_PTR)TrapFrame;
+    HalpEndSoftwareInterrupt(CurrentIrql);
+    
+    /* Exit the interrupt */
+    KiEoiHelper(TrapFrame);   
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
+{
+    /* Do the work */
+    _HalpDispatchInterruptHandler(TrapFrame);
+}
+
+VOID
+FASTCALL
+DECLSPEC_NORETURN
+HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame)
+{  
+    /* Set up a fake INT Stack */
+    TrapFrame->EFlags = __readeflags();
+    TrapFrame->SegCs = KGDT_R0_CODE;
+    TrapFrame->Eip = TrapFrame->Eax;
+    
+    /* Build the trap frame */
+    KiEnterInterruptTrap(TrapFrame);
+    
+    /* Do the work */
+    _HalpDispatchInterruptHandler(TrapFrame);
+}
+
+KiTrap(HalpApcInterrupt,      KI_SOFTWARE_TRAP);
+KiTrap(HalpDispatchInterrupt, KI_SOFTWARE_TRAP);