Windows2000/private/ntos/ke/ppc/clock.s
2020-09-30 17:12:32 +02:00

640 lines
24 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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