Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

639 lines
24 KiB

// 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)