/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: ntoskrnl/ke/powerpc/ppc_irq.c * PURPOSE: IRQ handling * * PROGRAMMERS: David Welch (welch@mcmail.com) */ /* * NOTE: In general the PIC interrupt priority facilities are used to * preserve the NT IRQL semantics, global interrupt disables are only used * to keep the PIC in a consistent state * */ /* INCLUDES ****************************************************************/ #include #include #define NDEBUG #include KDPC KiExpireTimerDpc; extern ULONG KiMaximumDpcQueueDepth; extern ULONG KiMinimumDpcRate; extern ULONG KiAdjustDpcThreshold; extern ULONG KiIdealDpcRate; extern LONG KiTickOffset; extern ULONG KeMaximumIncrement; extern ULONG KeMinimumIncrement; extern ULONG KeTimeAdjustment; extern void PearPCDebug(int ch); /* GLOBALS *****************************************************************/ /* Interrupt handler list */ #define NR_TRAPS 16 #ifdef CONFIG_SMP #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum #define BUILD_INTERRUPT_HANDLER(intnum) \ VOID INT_NAME2(intnum)(VOID); #define D(x,y) \ BUILD_INTERRUPT_HANDLER(x##y) #define D16(x) \ D(x,0) D(x,1) D(x,2) D(x,3) \ D(x,4) D(x,5) D(x,6) D(x,7) \ D(x,8) D(x,9) D(x,A) D(x,B) \ D(x,C) D(x,D) D(x,E) D(x,F) D16(3) D16(4) D16(5) D16(6) D16(7) D16(8) D16(9) D16(A) D16(B) D16(C) D16(D) D16(E) D16(F) #define L(x,y) \ (ULONG)& INT_NAME2(x##y) #define L16(x) \ L(x,0), L(x,1), L(x,2), L(x,3), \ L(x,4), L(x,5), L(x,6), L(x,7), \ L(x,8), L(x,9), L(x,A), L(x,B), \ L(x,C), L(x,D), L(x,E), L(x,F) static ULONG irq_handler[ROUND_UP(NR_TRAPS, 16)] = { L16(3), L16(4), L16(5), L16(6), L16(7), L16(8), L16(9), L16(A), L16(B), L16(C), L16(D), L16(E) }; #undef L #undef L16 #undef D #undef D16 #else /* CONFIG_SMP */ void trap_handler_0(void); void trap_handler_1(void); void trap_handler_2(void); void trap_handler_3(void); void trap_handler_4(void); void trap_handler_5(void); void trap_handler_6(void); void trap_handler_7(void); void trap_handler_8(void); void trap_handler_9(void); void trap_handler_10(void); void trap_handler_11(void); void trap_handler_12(void); void trap_handler_13(void); void trap_handler_14(void); void trap_handler_15(void); static unsigned int trap_handler[NR_TRAPS] __attribute__((unused)) = { (int)&trap_handler_0, (int)&trap_handler_1, (int)&trap_handler_2, (int)&trap_handler_3, (int)&trap_handler_4, (int)&trap_handler_5, (int)&trap_handler_6, (int)&trap_handler_7, (int)&trap_handler_8, (int)&trap_handler_9, (int)&trap_handler_10, (int)&trap_handler_11, (int)&trap_handler_12, (int)&trap_handler_13, (int)&trap_handler_14, (int)&trap_handler_15, }; #endif /* CONFIG_SMP */ /* * PURPOSE: Object describing each isr * NOTE: The data in this table is only modified at passsive level but can * be accessed at any irq level. */ typedef struct { LIST_ENTRY ListHead; KSPIN_LOCK Lock; ULONG Count; } ISR_TABLE, *PISR_TABLE; #ifdef CONFIG_SMP static ISR_TABLE IsrTable[NR_TRAPS][MAXIMUM_PROCESSORS]; #else static ISR_TABLE IsrTable[NR_TRAPS][1]; #endif #define TAG_ISR_LOCK 'LRSI' /* FUNCTIONS ****************************************************************/ VOID INIT_FUNCTION NTAPI KeInitInterrupts (VOID) { int i, j; /* * Setup the IDT entries to point to the interrupt handlers */ for (i=0;iCurrentThread)) return; /* Get the current thread and process */ CurrentThread = Prcb->CurrentThread; CurrentProcess = CurrentThread->ApcState.Process; /* Check if we came from user mode */ if (TrapFrame->PreviousMode != KernelMode) { /* Update user times */ CurrentThread->UserTime++; InterlockedIncrement((PLONG)&CurrentProcess->UserTime); Prcb->UserTime++; } else { /* Check IRQ */ if (Irql > DISPATCH_LEVEL) { /* This was an interrupt */ Prcb->InterruptTime++; } else if ((Irql < DISPATCH_LEVEL) || !(Prcb->DpcRoutineActive)) { /* This was normal kernel time */ CurrentThread->KernelTime++; InterlockedIncrement((PLONG)&CurrentProcess->KernelTime); } else if (Irql == DISPATCH_LEVEL) { /* This was DPC time */ Prcb->DpcTime++; } /* Update CPU kernel time in all cases */ Prcb->KernelTime++; } /* Set the last DPC Count and request rate */ Prcb->DpcLastCount = Prcb->DpcData[0].DpcCount; Prcb->DpcRequestRate = ((Prcb->DpcData[0].DpcCount - Prcb->DpcLastCount) + Prcb->DpcRequestRate) / 2; /* Check if we should request a DPC */ if ((Prcb->DpcData[0].DpcQueueDepth) && !(Prcb->DpcRoutineActive)) { /* Request one */ HalRequestSoftwareInterrupt(DISPATCH_LEVEL); /* Update the depth if needed */ if ((Prcb->DpcRequestRate < KiIdealDpcRate) && (Prcb->MaximumDpcQueueDepth > 1)) { /* Decrease the maximum depth by one */ Prcb->MaximumDpcQueueDepth--; } } else { /* Decrease the adjustment threshold */ if (!(--Prcb->AdjustDpcThreshold)) { /* We've hit 0, reset it */ Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold; /* Check if we've hit queue maximum */ if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth) { /* Increase maximum by one */ Prcb->MaximumDpcQueueDepth++; } } } /* * If we're at end of quantum request software interrupt. The rest * is handled in KiDispatchInterrupt. * * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine * which checks for quantum end will not be executed and decrementing * the quantum here can result in overflow. This is not a problem since * we don't care about the quantum value anymore after the QuantumEnd * flag is set. */ if ((CurrentThread->Quantum -= 3) <= 0) { Prcb->QuantumEnd = TRUE; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } } /* * NOTE: On Windows this function takes exactly zero parameters and EBP is * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL. * The function is used only by HAL, so there's no point in keeping * that prototype. * * @implemented */ VOID NTAPI KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame, IN KIRQL Irql, IN ULONG Increment) { LONG OldOffset; LARGE_INTEGER Time; ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL); /* Update interrupt time */ Time.LowPart = SharedUserData->InterruptTime.LowPart; Time.HighPart = SharedUserData->InterruptTime.High1Time; Time.QuadPart += Increment; SharedUserData->InterruptTime.High2Time = Time.u.HighPart; SharedUserData->InterruptTime.LowPart = Time.u.LowPart; SharedUserData->InterruptTime.High1Time = Time.u.HighPart; /* Increase the tick offset */ KiTickOffset -= Increment; OldOffset = KiTickOffset; /* Check if this isn't a tick yet */ if (KiTickOffset > 0) { /* Expire timers */ KeInsertQueueDpc(&KiExpireTimerDpc, 0, 0); } else { /* Setup time structure for system time */ Time.LowPart = SharedUserData->SystemTime.LowPart; Time.HighPart = SharedUserData->SystemTime.High1Time; Time.QuadPart += KeTimeAdjustment; SharedUserData->SystemTime.High2Time = Time.HighPart; SharedUserData->SystemTime.LowPart = Time.LowPart; SharedUserData->SystemTime.High1Time = Time.HighPart; /* Setup time structure for tick time */ Time.LowPart = KeTickCount.LowPart; Time.HighPart = KeTickCount.High1Time; Time.QuadPart += 1; KeTickCount.High2Time = Time.HighPart; KeTickCount.LowPart = Time.LowPart; KeTickCount.High1Time = Time.HighPart; SharedUserData->TickCount.High2Time = Time.HighPart; SharedUserData->TickCount.LowPart = Time.LowPart; SharedUserData->TickCount.High1Time = Time.HighPart; /* Queue a DPC that will expire timers */ KeInsertQueueDpc(&KiExpireTimerDpc, 0, 0); } /* Update process and thread times */ if (OldOffset <= 0) { /* This was a tick, calculate the next one */ KiTickOffset += KeMaximumIncrement; KeUpdateRunTime(TrapFrame, Irql); } } VOID NTAPI KiInterruptDispatch2 (ULONG vector, KIRQL old_level) /* * FUNCTION: Calls all the interrupt handlers for a given irq. * ARGUMENTS: * vector - The number of the vector to call handlers for. * old_level - The irql of the processor when the irq took place. * NOTES: Must be called at DIRQL. */ { PKINTERRUPT isr; PLIST_ENTRY current; KIRQL oldlvl; PISR_TABLE CurrentIsr; DPRINT("I(0x%.08x, 0x%.08x)\n", vector, old_level); /* * Iterate the list until one of the isr tells us its device interrupted */ CurrentIsr = &IsrTable[vector][(ULONG)KeGetCurrentProcessorNumber()]; KiAcquireSpinLock(&CurrentIsr->Lock); CurrentIsr->Count++; current = CurrentIsr->ListHead.Flink; while (current != &CurrentIsr->ListHead) { isr = CONTAINING_RECORD(current,KINTERRUPT,InterruptListEntry); oldlvl = KeAcquireInterruptSpinLock(isr); if (isr->ServiceRoutine(isr, isr->ServiceContext)) { KeReleaseInterruptSpinLock(isr, oldlvl); break; } KeReleaseInterruptSpinLock(isr, oldlvl); current = current->Flink; } KiReleaseSpinLock(&CurrentIsr->Lock); } VOID KiInterruptDispatch3 (ULONG vector, PKIRQ_TRAPFRAME Trapframe) /* * FUNCTION: Calls the irq specific handler for an irq * ARGUMENTS: * irq = IRQ that has interrupted */ { KIRQL old_level; KTRAP_FRAME KernelTrapFrame; PKTHREAD CurrentThread; PKTRAP_FRAME OldTrapFrame=NULL; /* * At this point we have interrupts disabled, nothing has been done to * the PIC. */ KeGetCurrentPrcb()->InterruptCount++; /* * Notify the rest of the kernel of the raised irq level. For the * default HAL this will send an EOI to the PIC and alter the IRQL. */ if (!HalBeginSystemInterrupt (vector, vector, &old_level)) { return; } /* * Enable interrupts * NOTE: Only higher priority interrupts will get through */ _enable(); #ifndef CONFIG_SMP if (vector == 0) { KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame); KeUpdateSystemTime(&KernelTrapFrame, old_level, 100000); } else #endif { /* * Actually call the ISR. */ KiInterruptDispatch2(vector, old_level); } /* * End the system interrupt. */ _disable(); if (old_level==PASSIVE_LEVEL) { HalEndSystemInterrupt (APC_LEVEL, 0); CurrentThread = KeGetCurrentThread(); if (CurrentThread!=NULL && CurrentThread->ApcState.UserApcPending) { if (CurrentThread->TrapFrame == NULL) { OldTrapFrame = CurrentThread->TrapFrame; KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame); CurrentThread->TrapFrame = &KernelTrapFrame; } _enable(); KiDeliverApc(UserMode, NULL, NULL); _disable(); ASSERT(KeGetCurrentThread() == CurrentThread); if (CurrentThread->TrapFrame == &KernelTrapFrame) { KeTrapFrameToIRQTrapFrame(&KernelTrapFrame, Trapframe); CurrentThread->TrapFrame = OldTrapFrame; } } KeLowerIrql(PASSIVE_LEVEL); } else { HalEndSystemInterrupt (old_level, 0); } } static VOID KeDumpIrqList(VOID) { PKINTERRUPT current; PLIST_ENTRY current_entry; LONG i, j; KIRQL oldlvl; BOOLEAN printed; for (i=0;iFlink; current = CONTAINING_RECORD(current_entry,KINTERRUPT,InterruptListEntry); } KiReleaseSpinLock(&IsrTable[i][j].Lock); } KeLowerIrql(oldlvl); } } /* * @implemented */ BOOLEAN NTAPI KeConnectInterrupt(PKINTERRUPT InterruptObject) { KIRQL oldlvl,synch_oldlvl; PKINTERRUPT ListHead; ULONG Vector; PISR_TABLE CurrentIsr; BOOLEAN Result; DPRINT("KeConnectInterrupt()\n"); Vector = InterruptObject->Vector; if (Vector < 0 || Vector >= NR_TRAPS) return FALSE; ASSERT (InterruptObject->Number < KeNumberProcessors); KeSetSystemAffinityThread(1 << InterruptObject->Number); CurrentIsr = &IsrTable[Vector][(ULONG)InterruptObject->Number]; KeRaiseIrql(Vector,&oldlvl); KiAcquireSpinLock(&CurrentIsr->Lock); /* * Check if the vector is already in use that we can share it */ if (!IsListEmpty(&CurrentIsr->ListHead)) { ListHead = CONTAINING_RECORD(CurrentIsr->ListHead.Flink,KINTERRUPT,InterruptListEntry); if (InterruptObject->ShareVector == FALSE || ListHead->ShareVector==FALSE) { KiReleaseSpinLock(&CurrentIsr->Lock); KeLowerIrql(oldlvl); KeRevertToUserAffinityThread(); return FALSE; } } synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject); DPRINT("%x %x\n",CurrentIsr->ListHead.Flink, CurrentIsr->ListHead.Blink); Result = HalEnableSystemInterrupt(Vector, InterruptObject->Irql, InterruptObject->Mode); if (Result) { InsertTailList(&CurrentIsr->ListHead,&InterruptObject->InterruptListEntry); DPRINT("%x %x\n",InterruptObject->InterruptListEntry.Flink, InterruptObject->InterruptListEntry.Blink); } InterruptObject->Connected = TRUE; KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl); /* * Release the table spinlock */ KiReleaseSpinLock(&CurrentIsr->Lock); KeLowerIrql(oldlvl); KeDumpIrqList(); KeRevertToUserAffinityThread(); return Result; } /* * @implemented * * FUNCTION: Releases a drivers isr * ARGUMENTS: * InterruptObject = isr to release */ BOOLEAN NTAPI KeDisconnectInterrupt(PKINTERRUPT InterruptObject) { KIRQL oldlvl,synch_oldlvl; PISR_TABLE CurrentIsr; BOOLEAN State; DPRINT1("KeDisconnectInterrupt\n"); ASSERT (InterruptObject->Number < KeNumberProcessors); /* Set the affinity */ KeSetSystemAffinityThread(1 << InterruptObject->Number); /* Get the ISR Tabe */ CurrentIsr = &IsrTable[InterruptObject->Vector] [(ULONG)InterruptObject->Number]; /* Raise IRQL to required level and lock table */ KeRaiseIrql(InterruptObject->Vector,&oldlvl); KiAcquireSpinLock(&CurrentIsr->Lock); /* Check if it's actually connected */ if ((State = InterruptObject->Connected)) { /* Lock the Interrupt */ synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject); /* Remove this one, and check if all are gone */ RemoveEntryList(&InterruptObject->InterruptListEntry); if (IsListEmpty(&CurrentIsr->ListHead)) { /* Completely Disable the Interrupt */ HalDisableSystemInterrupt(InterruptObject->Vector, InterruptObject->Irql); } /* Disconnect it */ InterruptObject->Connected = FALSE; /* Release the interrupt lock */ KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl); } /* Release the table spinlock */ KiReleaseSpinLock(&CurrentIsr->Lock); KeLowerIrql(oldlvl); /* Go back to default affinity */ KeRevertToUserAffinityThread(); /* Return Old Interrupt State */ return State; } /* * @implemented */ VOID NTAPI KeInitializeInterrupt(PKINTERRUPT Interrupt, PKSERVICE_ROUTINE ServiceRoutine, PVOID ServiceContext, PKSPIN_LOCK SpinLock, ULONG Vector, KIRQL Irql, KIRQL SynchronizeIrql, KINTERRUPT_MODE InterruptMode, BOOLEAN ShareVector, CHAR ProcessorNumber, BOOLEAN FloatingSave) { /* Set the Interrupt Header */ Interrupt->Type = InterruptObject; Interrupt->Size = sizeof(KINTERRUPT); /* Check if we got a spinlock */ if (SpinLock) { Interrupt->ActualLock = SpinLock; } else { /* This means we'll be usin the built-in one */ KeInitializeSpinLock(&Interrupt->SpinLock); Interrupt->ActualLock = &Interrupt->SpinLock; } /* Set the other settings */ Interrupt->ServiceRoutine = ServiceRoutine; Interrupt->ServiceContext = ServiceContext; Interrupt->Vector = Vector; Interrupt->Irql = Irql; Interrupt->SynchronizeIrql = SynchronizeIrql; Interrupt->Mode = InterruptMode; Interrupt->ShareVector = ShareVector; Interrupt->Number = ProcessorNumber; Interrupt->FloatingSave = FloatingSave; /* Disconnect it at first */ Interrupt->Connected = FALSE; } VOID KePrintInterruptStatistic(VOID) { LONG i, j; for (j = 0; j < KeNumberProcessors; j++) { DPRINT1("CPU%d:\n", j); for (i = 0; i < NR_TRAPS; i++) { if (IsrTable[i][j].Count) { DPRINT1(" Irq %x(%d): %d\n", i, i, IsrTable[i][j].Count); } } } } BOOLEAN NTAPI KeDisableInterrupts(VOID) { ULONG Flags = 0; BOOLEAN Return; Flags = __readmsr(); Return = (Flags & 0x8000) ? TRUE: FALSE; /* Disable interrupts */ _disable(); return Return; } ULONG NTAPI KdpServiceDispatcher(ULONG Service, PVOID Buffer1, ULONG Buffer1Length, KPROCESSOR_MODE PreviousMode); typedef ULONG (*PSYSCALL_FUN) (ULONG,ULONG,ULONG,ULONG,ULONG,ULONG,ULONG,ULONG,ULONG,ULONG); VOID NTAPI KiSystemService(ppc_trap_frame_t *trap_frame) { int i; PKSYSTEM_ROUTINE SystemRoutine; PSYSCALL_FUN SyscallFunction; switch(trap_frame->gpr[0]) { case 0x10000: /* DebugService */ for( i = 0; i < trap_frame->gpr[5]; i++ ) { PearPCDebug(((PCHAR)trap_frame->gpr[4])[i]); WRITE_PORT_UCHAR((PVOID)0x800003f8, ((PCHAR)trap_frame->gpr[4])[i]); } trap_frame->gpr[3] = KdpServiceDispatcher (trap_frame->gpr[3], (PCHAR)trap_frame->gpr[4], trap_frame->gpr[5], UserMode /*KernelMode*/); break; case 0xf0000: /* Thread startup */ /* XXX how to use UserThread (gpr[6]) */ SystemRoutine = (PKSYSTEM_ROUTINE)trap_frame->gpr[3]; SystemRoutine((PKSTART_ROUTINE)trap_frame->gpr[4], (PVOID)trap_frame->gpr[5]); break; /* Handle a normal system call */ default: SyscallFunction = ((PSYSCALL_FUN*)KeServiceDescriptorTable [trap_frame->gpr[0] >> 12].Base)[trap_frame->gpr[0] & 0xfff]; trap_frame->gpr[3] = SyscallFunction (trap_frame->gpr[3], trap_frame->gpr[4], trap_frame->gpr[5], trap_frame->gpr[6], trap_frame->gpr[7], trap_frame->gpr[8], trap_frame->gpr[9], trap_frame->gpr[10], trap_frame->gpr[11], trap_frame->gpr[12]); break; } } /* EOF */