// TITLE("Interval and Profile Clock Interrupts") //++ // // Copyright (c) 1990 Microsoft Corporation // // Module Name: // // xxclock.s // // Abstract: // // This module implements the code necessary to field and process the // interval and profile clock interrupts. // // Author: // // Chris P. Karamatas 27-Sep-1993 // // Based on MIPS version by David N. Cutler (davec) 27-Mar-1990 // // Modified by: // // Pat Carr 14-Apr-1994 to follow 3.5 model // Peter Johnston 30-May-1994 extensive mods for level 612 // Peter Johnston 10-Jun-1994 updated to level 683 (Beta 2) // // Environment: // // Kernel mode only. // // Revision History: // //-- .file "clock.s" #include "ksppc.h" // // Define external variables used by this module. // .extern KeTickCount // 3 * 4 .extern KeTimeAdjustment // 4 .extern KiAdjustDpcThreshold // 4 .extern KiIdealDpcRate // 4 .extern KiMaximumDpcQueueDepth // 4 .extern KiProfileListHead // 2 * 4 .extern KiProfileLock // 4 .extern KiTimerTableListHead .extern KiTimerExpireDpc .extern KiTickOffset .extern KeMaximumIncrement #if !defined(NT_UP) && SPINDBG .extern ..KiAcquireSpinLockDbg #endif //++ // // VOID // KeUpdateSystemTime ( // IN PKTRAP_FRAME TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt generated by the // interval timer. Its function is to update the system time and check to // determine if a timer has expired. // // N.B. This routine is executed on a single processor in a multiprocess // system. The remainder of the processors only execute the quantum end // and runtime update code. // // Arguments: // // TrapFrame (r.3) - Supplies a pointer to a trap frame. // TimeIncrement (r.4) - Supplies the rime increment in 100ns units. // // Return Value: // // None. // //-- // rem hal calls this with interrupts disabled .set cr.5.gt, 21 .set cr.0.gt, 1 LEAF_ENTRY(KeUpdateSystemTime) // // Update the interrupt time. // // N.B. The interrupt time is updated in a very strict manner so that an // interlock does not have to be used in an MP system. // lwz r.5, [toc]KiTickOffset(r.toc) // get addr of globals used // from "Compute next tick lwz r.12, [toc]KeMaximumIncrement(r.toc) // offset value." lwz r.7, KiPcr2 + Pc2InterruptTime + 0(r.0) // get low interrupt time lwz r.8, KiPcr2 + Pc2InterruptTime + 4(r.0) // get high interrupt time lwz r.10, [toc]KeTickCount(r.toc) lwz r.11, [toc]KiTimerTableListHead(r.toc) lwz r.9, 0(r.5) // get tick offset value lwz r.12, 0(r.12) // get maximum increment value addc r.7, r.7, r.4 // add time increment value addze r.8, r.8 // increment high interupt time stw r.8, KiPcr2 + Pc2InterruptTime + 8(r.0) // store high 2 interrupt time stw r.7, KiPcr2 + Pc2InterruptTime + 0(r.0) // store low interrupt time stw r.8, KiPcr2 + Pc2InterruptTime + 4(r.0) // store high 1 interrupt time sub. r.9, r.9, r.4 // subtract time increment // from "Compute next tick lwz r.4, [toc]KeTimeAdjustment(r.toc) // offset value." stw r.9, 0(r.5) // store tick offset value add r.9, r.12, r.9 // add maximum inc to residue crmove cr.5.gt, cr.0.gt // save cr.0 gt for later lwz r.6, 0(r.10) // get low tick count bgt check_timer // jif tick not completed // // Compute next tick offset value. // stw r.9, 0(r.5) // store tick offset value lwz r.4, 0(r.4) // get time adjustment value // // Update system time. // // N.B. The system time is updated in a very strict manner so that an // interlock does not have to be used in an MP system. // lwz r.0, KiPcr2 + Pc2SystemTime + 0(r.0) // get low system time lwz r.9, KiPcr2 + Pc2SystemTime + 4(r.0) // get high system time addc r.0, r.4, r.0 // add time increment // From "Update the tick count" lwz r.4, 4(r.10) // get high tick count addze r.9, r.9 // incremnt high system time stw r.9, KiPcr2 + Pc2SystemTime + 8(r.0) // store high 2 system time stw r.0, KiPcr2 + Pc2SystemTime + 0(r.0) // store low system time stw r.9, KiPcr2 + Pc2SystemTime + 4(r.0) // store high 1 system time // From "Update the tick count" addic r.9, r.6, 1 // increment tick count // // Update the tick count. // // N.B. The tick count is updated in a very strict manner so that an // interlock does not have to be used in an MP system. // addze r.4, r.4 // increment high word stw r.9, KiPcr2 + Pc2TickCountLow(r.0) // store low tick count stw r.4, 8(r.10) // store high 2 tick count stw r.9, 0(r.10) // store low tick count stw r.4, 4(r.10) // store high 1 tick count // // Check to determine if a timer has expired at the current (ie old) hand // value. // rlwinm r.10, r.6, 3, (TIMER_TABLE_SIZE - 1) << 3 // get table offset add r.10, r.11, r.10 // get addr of table entry lwz r.9, LsFlink(r.10) // get addr of 1st timer in list cmplw cr7, r.9, r.10 // list empty? // From "Get the expiration..." lwz r.4, TiDueTime + TmHighTime - TiTimerListEntry(r.9) // From "Get the expiration..." lwz r.5, TiDueTime + TmLowTime - TiTimerListEntry(r.9) beq cr7, check_next_hand // jif yes // // Get the expiration time from the timer object. // // N.B. The offset to the timer list entry must be subtracted out of the // displacement calculation. // cmplw cr.6, r.4, r.8 // check high time cmplw cr.7, r.5, r.7 // check low time bgt cr.6, check_next_hand // this timer has not expired blt cr.6, expire // this timer has expired ble cr.7, expire // this timer has expired // // Check to determine if a timer has expired at the next hand value. // check_next_hand: addi r.6, r.6, 1 // advance hand entry to next check_timer: rlwinm r.10, r.6, 3, (TIMER_TABLE_SIZE - 1) << 3 // get table offset add r.10, r.11, r.10 // get addr of table entry lwz r.9, LsFlink(r.10) // get addr of 1st timer in list cmplw cr.7, r.9, r.10 // list empty? lwz r.5, TiDueTime + TmLowTime - TiTimerListEntry(r.9) beq cr.7, kust_exit // jif yes // Get the expiration time from the timer object. // // N.B. The offset to the timer list entry must be subtracted out of the // displacement calculation. // // Note we can't move this guy // above beq kust_exit. lwz r.4, TiDueTime + TmHighTime - TiTimerListEntry(r.9) cmplw cr.6, r.4, r.8 // check high time cmplw cr.7, r.5, r.7 // check low time bgt cr.6, kust_exit // this timer has not expired blt cr.6, expire // this timer has expired bgt cr.7, kust_exit // this timer has not expired // // Put timer expiration DPC in the system DPC list and initiate a dispatch // interrupt on the current processor. // expire: lwz r.9, KiPcr+PcPrcb(r.0) // get address of PRCB lwz r.10, [toc]KiTimerExpireDpc(r.toc)// get expiration DPC address addi r.11, r.9, PbDpcListHead // compute DPC listhead address addi r.7, r.9, PbDpcLock // compute DPC lock address #if !defined(NT_UP) ACQUIRE_SPIN_LOCK(r.7, r.11, r.0, expire_lock, expire_lock_spin) #endif lwz r.8, DpLock(r.10) // get DPC inserted state addi r.5, r.10, DpDpcListEntry // compute addr DPC list entry lwz r.12, LsBlink(r.11) // get addr last entry in list cmplwi r.8, 0 // DPC inserted? bne queued // jif DPC already inserted lwz r.8, PbDpcQueueDepth(r.9) // get DPC queue depth stw r.7, DpLock(r.10) // set DPC inserted state stw r.6, DpSystemArgument1(r.10) // set timer table hand value stw r.5, LsBlink(r.11) // set addr of new last entry stw r.5, LsFlink(r.12) // set next in old last entry stw r.11, LsFlink(r.5) // set address of next entry stw r.12, LsBlink(r.5) // set address of previous entry addi r.8, r.8, 1 // increment DPC queue depth stw r.8, PbDpcQueueDepth(r.9) // SOFTWARE_INTERRUPT(DISPATCH_LEVEL, r.4) queued: #if !defined(NT_UP) RELEASE_SPIN_LOCK(r.7, r.0) #endif kust_exit: ble cr.5, ..KeUpdateRunTime // if lez, full tick blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r.7, r.0, expire_lock, expire_lock_spin) #endif DUMMY_EXIT(KeUpdateSystemTime) //++ // // VOID // KeUpdateRunTime ( // IN PKTRAP_FRAME TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt generated by the // interval timer. Its function is to update the runtime of the current // thread, update the runtime of the current thread's process, and decrement // the current thread's quantum. // // N.B. This routine is executed on all processors in a multiprocess system. // // Arguments: // // TrapFrame (r.3) - Supplies a pointer to a trap frame. // // Return Value: // // None. // //-- LEAF_ENTRY(KeUpdateRunTime) lwz r.9, KiPcr+PcPrcb(r.0) // get current processor block lwz r.7, TrMsr(r.3) // get saved machine status // From "If a DPC is activ..." lbz r.10, TrOldIrql(r.3) // get previous IRQL lwz r.4, KiPcr+PcCurrentThread(r.0) // get current thread address lwz r.8, ThUserTime(r.4) // get user time lwz r.11, PbDpcRoutineActive(r.9) // get DPC active flag extrwi. r.7, r.7, 1, MSR_PR // test previous mode lwz r.6, ThApcState + AsProcess(r.4)// get addr current process cmpwi cr.1, r.11, 0 // DPC active ? bne user // jif previous mode was user // // If a DPC is active, then increment the time spent executing DPC routines. // Otherwise, if the old IRQL is greater than DPC level, then increment the // time spent executing interrupt services routines. Otherwise, increment // the time spent in kernel mode for the current thread. // lwz r.11, ThKernelTime(r.4) // get kernel time cmpwi r.10, DISPATCH_LEVEL // compare IRQL with DPC level addi r.7, r.9, PbInterruptTime // compute interrupt time addr blt kernel // if ltz, inc. thread kernel time bgt intrpt // if >DPC level bump interrupt addi r.7, r.9, PbDpcTime // compute DPC time address beq cr.1, kernel // jif not dpc active // // Update the time spent in DPC/interrupt processing. // intrpt: lwz r.8, 0(r.7) // get processor time addi r.5, r.9, PbKernelTime // compute processor kernel time addr. addi r.8, r.8, 1 // increment processor time stw r.8, 0(r.7) // store processor time b processor // // Update the time spent in kernel mode for the current thread. // kernel: addi r.6, r.6, PrKernelTime // compute process kernel time addr. addi r.5, r.9, PbKernelTime // compute processor kernel time addr. addi r.11, r.11, 1 // increment kernel time stw r.11, ThKernelTime(r.4) // store kernel time b continue // // Update the time spent in user mode for the current thread. // user: addi r.6, r.6, PrUserTime // compute process user time addr. addi r.5, r.9, PbUserTime // compute processor user time addr. addi r.8, r.8, 1 // increment user time stw r.8, ThUserTime(r.4) // store user time // // Update the time spent in kernel/user mode for the current thread's process. // // N.B. The update of the process time must be synchronized across processors. // //DBGSTORE_I(r10,r8,0x1112) continue: lwarx r.10, 0, r.6 // get process time addi r.10, r.10, 1 // increment process time stwcx. r.10, 0, r.6 // store process time bne- continue // if store conditional failed // // Update the time spent in kernel/user mode for the current processor. // processor: lwz r.10, 0(r.5) // get low processor time // // Update the DPC request rate which is computed as the average between // the previous rate and the current rate. // lwz r.8, PbDpcQueueDepth(r.9) // get current DPC queue depth lwz r.6, PbDpcLastCount(r.9) // get last DPC count lwz r.7, PbDpcRequestRate(r.9)// get last DPC request rate lwz r.0, PbDpcInterruptRequested(r.9)// interrupt requested? // NOTE: we can't move these below // the next lwz to r.5 addi r.10, r.10, 1 // increment processor time stw r.10, 0(r.5) // store low processor time lwz r.5, PbDpcCount(r.9) // get current DPC count cmpwi cr.0, r.8, 0 lwz r.8, [toc]KiAdjustDpcThreshold(r.toc) stw r.5, PbDpcLastCount(r.9) // set last DPC count sub r.5, r.5, r.6 // compute count during interval cmpwi cr.7, r.0, 0 // interrupt requested ? add r.5, r.5, r.7 // compute sum of current and last srwi r.5, r.5, 1 // average current and last stw r.5, PbDpcRequestRate(r.9)// set new DPC request rate lwz r.0, 0(r.8) // get DPC threshold counter lwz r.7, PbMaximumDpcQueueDepth(r.9)// get current max queue depth // // If the current DPC queue depth is not zero, a DPC routine is not active, // and a DPC interrupt has not been requested, then request a dispatch // interrupt, decrement the maximum DPC queue depth, and reset the threshold // counter if appropriate. // bne cr.1, nodpc // jif DPC routine active beq cr.0, nodpc // jif DPC queue is empty bne cr.7, nodpc // jif DPC already requested lwz r.6, [toc]KiIdealDpcRate(r.toc) // get &ideal DPC rate stw r.0, PbAdjustDpcThreshold(r.9) // set new DPC threshold counter lwz r.6, 0(r.6) // get ideal DPC rate li r.0, 1 stb r.0, KiPcr+PcDispatchInterrupt(r.0) // request DPC interrupt cmpw cr.6, r.5, r.6 // current rate < ideal ? subic. r.7, r.7, 1 // decrement max DPC queue depth bge cr.6, ..KiDecrementQuantum // jif rate >= ideal beq ..KiDecrementQuantum // if cur val == 1 stw r.7, PbMaximumDpcQueueDepth(r.9)// set new maximum queue depth b ..KiDecrementQuantum // // The DPC queue is empty or a DPC routine is active or a DPC interrupt // has been requested. Count down the adjustment threshold and if the // count reaches zero, then increment the maximum DPC queue depth, but // no above the initial value and reset the adjustment threshold value. // nodpc: lwz r.5, [toc]KiMaximumDpcQueueDepth(r.toc) lwz r.6, PbAdjustDpcThreshold(r.9) // get adjustment threshold lwz r.5, 0(r.5) // get init max queue depth subic. r.6, r.6, 1 // decrement adjustment stw r.6, PbAdjustDpcThreshold(r.9) // threshold counter bne ..KiDecrementQuantum cmpw cr.6, r.5, r.7 stw r.0, PbAdjustDpcThreshold(r.9) // reset threshold beq cr.6, ..KiDecrementQuantum // jif at max depth addi r.7, r.7, 1 // increment current max depth stw r.7, PbMaximumDpcQueueDepth(r.9)// set new maximum DPC queue // depth. // // Decrement current thread quantum and check to determine if a quantum end // has occurred. // ALTERNATE_ENTRY(KiDecrementQuantum) lbz r.5, ThQuantum(r.4) // get current thread quantum extsb r.5, r.5 // sign-extend thread quantum subic. r.5, r.5, CLOCK_QUANTUM_DECREMENT // decrement current quantum stb r.5, ThQuantum(r.4) // store thread quantum bgtlr+ // return if quantum remaining // // Set quantum end flag and initiate a dispatch interrupt on the current // processor. // lwz r.5, PbIdleThread(r.9) // get address of idle thread cmpw r.5, r.4 // is this the idle thread? beqlr- // return if in idle thread stw r.sp, KiPcr+PcQuantumEnd(r.0) // set quantum end indicator SOFTWARE_INTERRUPT(DISPATCH_LEVEL, r.8) LEAF_EXIT(KeUpdateRunTime) //++ // // VOID // KeProfileInterruptWithSource ( // IN PKTRAP_FRAME TrapFrame, // IN KPROFILE_SOURCE ProfileSource // ) // // VOID // KeProfileInterrupt ( // IN PKTRAP_FRAME TrapFrame // ) // // Routine Description: // // This routine is entered as the result of an interrupt generated by the // profile timer. Its function is to update the profile information for // the currently active profile objects. // // N.B. This routine is executed on all processors in a multiprocess system. // // N.B. KeProfileInterruptWithSource currently not implemented // // Arguments: // // TrapFrame (r.3) - Supplies a pointer to a trap frame. // // ProfileSource (r.4) - Supplies the source of the profile interrupt // KeProfileInterrupt is an alternate entry for backwards // compatibility that sets the source to zero (ProfileTime) // // Return Value: // // None. // //-- .struct 0 .space StackFrameHeaderLength piLR: .space 4 // Link Register .align 3 // 8 byte align piFrameLength: SPECIAL_ENTRY(KeProfileInterrupt) li r.4, 0 // set profile source to // ProfileTime ALTERNATE_ENTRY(KeProfileInterruptWithSource) mflr r.0 stwu r.sp, -piFrameLength(r.sp) stw r.0, piLR(r.sp) // save return address PROLOGUE_END(KeProfileInterrupt) #if !defined(NT_UP) lwz r.11, [toc]KiProfileLock(r.toc) #endif #if !defined(NT_UP) ACQUIRE_SPIN_LOCK(r.11, r.3, r.10, profile_lock, profile_lock_spin) #endif lwz r.5, KiPcr+PcCurrentThread(r.0) // get current thread address lwz r.5, ThApcState + AsProcess(r.5)// get current process address addi r.5, r.5, PrProfileListHead // compute profile listhead addr bl ..KiProcessProfileList // process process profile list lwz r.5, [toc]KiProfileListHead(r.toc)// get profile listhead addr bl ..KiProcessProfileList // process system profile list lwz r.0, piLR(r.sp) // get return address #if !defined(NT_UP) lwz r.11, [toc]KiProfileLock(r.toc) li r.10, 0 RELEASE_SPIN_LOCK(r.11, r.10) #endif mtlr r.0 // set return address addi r.sp, r.sp, piFrameLength // deallocate stack frame blr #if !defined(NT_UP) SPIN_ON_SPIN_LOCK(r.11, r.10, profile_lock, profile_lock_spin) #endif DUMMY_EXIT(KeProfileInterrupt) //++ // // VOID // KiProcessProfileList ( // IN PKTRAP_FRAME TrapFrame, // IN KPROFILE_SOURCE Source, // IN PLIST_ENTRY ListHead // ) // // Routine Description: // // This routine is called to process a profile list. // // Arguments: // // TrapFrame (r.3) - Supplies a pointer to a trap frame. // // Source (r.4) - Supplies profile source to match // // ListHead (r.5) - Supplies a pointer to a profile list. // // Return Value: // // None. // // Note: // // Registers r.3 and r.4 are returned unaltered. // //-- LEAF_ENTRY(KiProcessProfileList) lwz r.6, LsFlink(r.5) // get address of next entry cmplw r.5, r.6 // cmp process profile list head beqlr // if eq, end of list lwz r.12, KiPcr+PcPrcb(r.0) // get address of PRCB lwz r.7, TrIar(r.3) // get interrupt PC address lwz r.12, PbSetMember(r.12) // get current processor num // // Scan profile list and increment profile buckets as appropriate. // l.10: lhz r.0, PfSource - PfProfileListEntry(r.6) // get source lwz r.8, PfRangeBase - PfProfileListEntry(r.6) // get range base cmplw cr.6, r.0, r.4 // compare source lwz r.9, PfRangeLimit - PfProfileListEntry(r.6)// get range limit lwz r.11, PfAffinity - PfProfileListEntry(r.6) // get affinity bne cr.6, l.20 // if ne, source mismatch cmplw cr.7, r.7, r.8 // check against range base cmplw cr.1, r.7, r.9 // check against range limit and. r.11, r.11, r.12 // check affinity blt cr.7, l.20 // jif less than range base bgt cr.1, l.20 // jif not less than range limit beq cr.0, l.20 // jif affinity mismatch sub r.8, r.7, r.8 // compute offset in range lwz r.9, PfBucketShift - PfProfileListEntry(r.6)// get shift count lwz r.10, PfBuffer - PfProfileListEntry(r.6) // get &profile buffer srw r.8, r.8, r.9 // compute bucket offset rlwinm r.8, r.8, 0, 0xfffffffc // clear low order offset bits lwzx r.7, r.8, r.10 // increment profile bucket addi r.7, r.7, 1 // stwx r.7, r.8, r.10 // l.20: lwz r.6, LsFlink(r.6) // get address of next entry cmplw r.5, r.6 // more entries in list ? bne l.10 // jif yes LEAF_EXIT(KiProcessProfileList)