NT4/private/ntos/ke/mips/xxclock.s
2020-09-30 17:12:29 +02:00

593 lines
21 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:
//
// David N. Cutler (davec) 27-Mar-1990
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksmips.h"
//
// Define external variables that can be addressed using GP.
//
.extern KeMaximumIncrement 4
.extern KeTickCount 3 * 4
.extern KeTimeAdjustment 4
.extern KiAdjustDpcThreshold 4
.extern KiIdealDpcRate 4
.extern KiMaximumDpcQueueDepth 4
.extern KiProfileListHead 2 * 4
.extern KiProfileLock 4
.extern KiTickOffset 4
SBTTL("Update System Time")
//++
//
// VOID
// KeUpdateSystemTime (
// IN PKTRAP_FRAME TrapFrame,
// IN ULONG TimeIncrement
// )
//
// 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 (a0) - Supplies a pointer to a trap frame.
//
// TimeIncrement (a1) - Supplies the time increment in 100ns units.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateSystemTime)
//
// Update the interrupt time.
//
ld t8,KiPcr2 + Pc2InterruptTime // get interrupt time
daddu t8,t8,a1 // add time increment value
sd t8,KiPcr2 + Pc2InterruptTime // store interrupt time
//
// Update tick offset and check for "system clock" tick.
//
lw a2,KiTickOffset // get tick offset value
sub a2,a2,a1 // subtract time increment
ld v0,KeTickCount // get low and high 1 tick count
la t0,KiTimerTableListHead // get base address of timer table
sw a2,KiTickOffset // store tick offset value
bgtz a2,10f // if gtz, tick not completed
lw a3,KeMaximumIncrement // get maximum increment value
//
// Update system time.
//
lw t1,KeTimeAdjustment // get time adjustment value
ld t2,KiPcr2 + Pc2SystemTime // get low and high 1 system time
daddu t2,t2,t1 // add time increment value
sd t2,KiPcr2 + Pc2SystemTime // store low nad high 1 system time
//
// 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. This is
// required for backward compatibility with old drivers and file
// systems.
//
daddu t2,v0,1 // increment tick count
dsrl t3,t2,32 // get high half of tick count
sw t2,KiPcr2 + Pc2TickCountLow(zero) // store low tick count
.set noreorder
.set noat
sw t3,KeTickCount + 8 // store high 2 tick count
sd t2,KeTickCount // store low and high 1 tick count
.set at
.set reorder
//
// Compute next tick offset value.
//
addu a3,a3,a2 // add maximum increment to residue
sw a3,KiTickOffset // store tick offset value
//
// Check to determine if a timer has expired at the current hand value.
//
and t1,v0,TIMER_TABLE_SIZE - 1 // reduce to table table index
sll t2,t1,3 // compute timer table listhead address
addu t2,t2,t0 //
lw t3,LsFlink(t2) // get address of first timer in list
beq t2,t3,5f // if eq, no timer active
//
// 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.
//
ld t4,TiDueTime - TiTimerListEntry(t3) // get timer due time
sltu t9,t8,t4 // check if timer is due
beq zero,t9,20f // if eq, timer has expired
//
// Check to determine if a timer has expired at the next hand value.
//
5: addu v0,v0,1 // advance hand value to next entry
10: and t1,v0,TIMER_TABLE_SIZE - 1 // reduce to table table index
sll t2,t1,3 // compute timer table listhead address
addu t2,t2,t0 //
lw t3,LsFlink(t2) // get address of first timer in list
beq t2,t3,40f // if eq, no timer active
//
// 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.
//
ld t4,TiDueTime - TiTimerListEntry(t3) // get timer due time
sltu t9,t8,t4 // check if timer is due
bne zero,t9,40f // if ne, timer has not expired
//
// Put timer expiration DPC in the system DPC list and initiate a dispatch
// interrupt on the current processor.
//
20: la t0,KiTimerExpireDpc // get expiration DPC address
lw a1,KiPcr + PcPrcb(zero) // get address of PRCB
DISABLE_INTERRUPTS(t2) // disable interrupts
addu t3,a1,PbDpcListHead // compute DPC listhead address
addu v1,a1,PbDpcLock // compute DPC lock address
#if !defined(NT_UP)
30: ll t4,0(v1) // get current lock value
move t5,t3 // set lock ownership value
bne zero,t4,30b // if ne, spin lock owned
sc t5,0(v1) // set spin lock owned
beq zero,t5,30b // if eq, store conditional failed
#endif
lw t4,DpLock(t0) // get DPC inserted state
bne zero,t4,35f // if ne, DPC entry already inserted
lw t4,LsBlink(t3) // get address of last entry in list
sw v1,DpLock(t0) // set DPC inserted state
sw v0,DpSystemArgument1(t0) // set timer table hand value
addu t0,t0,DpDpcListEntry // compute address of DPC list entry
sw t0,LsBlink(t3) // set address of new last entry
sw t0,LsFlink(t4) // set next link in old last entry
sw t3,LsFlink(t0) // set address of next entry
sw t4,LsBlink(t0) // set address of previous entry
lw t5,PbDpcQueueDepth(a1) // increment DPC queue depth
addu t5,t5,1 //
sw t5,PbDpcQueueDepth(a1) //
.set noreorder
.set noat
mfc0 t3,cause // get exception cause register
or t3,t3,DISPATCH_INTERRUPT // merge dispatch interrut request
mtc0 t3,cause // set exception cause register
.set at
.set reorder
35: //
#if !defined(NT_UP)
sw zero,0(v1) // set spin lock not owned
#endif
ENABLE_INTERRUPTS(t2) // enable interrupts
40: blez a2,50f // if lez, full tick
j ra // return
50: j KeUpdateRunTime
.end KeUpdateSystemTime
SBTTL("Update Thread and Process Runtime")
//++
//
// 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 (a0) - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateRunTime)
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
lw t2,ThApcState + AsProcess(t0) // get address of current process
lw t3,TrPsr(a0) // get saved processor status
lw t5,KiPcr + PcPrcb(zero) // get current processor block address
lw t7,PbDpcRoutineActive(t5) // get DPC active flag
and t4,t3,0x1 << PSR_PMODE // isolate previous processor mode
bne zero,t4,30f // if ne, 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.
//
lbu t6,TrOldIrql(a0) // get previous IRQL
subu t6,t6,DISPATCH_LEVEL // compare IRQL with DPC level
bltz t6,20f // if ltz, increment thread kernel time
addu t8,t5,PbInterruptTime // compute interrupt time address
bgtz t6,10f // if gtz, increment interrupt time
addu t8,t5,PbDpcTime // compute DPC time address
beq zero,t7,20f // if eq, increment thread kernel time
//
// Update the time spend in DPC/interrupt processing.
//
10: lw t6,0(t8) // get processor time
addu t6,t6,1 // increment processor time
sw t6,0(t8) // store processor time
addu t9,t5,PbKernelTime // compute processor kernel time address
b 50f //
//
// Update the time spent in kernel mode for the current thread.
//
20: lw t6,ThKernelTime(t0) // get kernel time
addu t6,t6,1 // increment kernel time
sw t6,ThKernelTime(t0) // store kernel time
addu t2,t2,PrKernelTime // compute process kernel time address
addu t9,t5,PbKernelTime // compute processor kernel time address
b 40f //
//
// Update the time spent in user mode for the current thread.
//
30: lw t6,ThUserTime(t0) // get user time
addu t6,t6,1 // increment user time
sw t6,ThUserTime(t0) // store user time
addu t2,t2,PrUserTime // compute process user time address
addu t9,t5,PbUserTime // compute processor user time address
//
// 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.
//
40: ll t6,0(t2) // get process time
addu t6,t6,1 // increment process time
sc t6,0(t2) // store process time
beq zero,t6,40b // if eq, store conditional failed
//
// Update the time spent in kernel/user mode for the current processor.
//
50: lw t6,0(t9) // get processor time
addu t6,t6,1 // increment processor time
sw t6,0(t9) // store processor time
//
// Update the DPC request rate which is computed as the average between
// the previous rate and the current rate.
//
lw a0,PbDpcCount(t5) // get current DPC count
lw a1,PbDpcLastCount(t5) // get last DPC count
lw a2,PbDpcRequestRate(t5) // get last DPC request rate
lw a3,PbDpcQueueDepth(t5) // get current DPC queue depth
sw a0,PbDpcLastCount(t5) // set last DPC count
subu a0,a0,a1 // compute count during interval
addu a0,a0,a2 // compute sum of current and last
srl a0,a0,1 // average current and last
sw a0,PbDpcRequestRate(t5) // set new DPC request rate
//
// 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.
//
lw v0,PbDpcInterruptRequested(t5) // get DPC interrupt requested
beq zero,a3,60f // if eq, DPC queue is empty
or v0,v0,t7 // merge DPC interrupt requested and active
bne zero,v0,60f // if ne, DPC active or interrupt requested
DISABLE_INTERRUPTS(a1) // disable interrupt
.set noreorder
.set noat
mfc0 a2,cause // get exception cause register
lw v0,PbMaximumDpcQueueDepth(t5) // get maximum queue depth
lw v1,KiIdealDpcRate // get ideal DPC rate
or a2,a2,DISPATCH_INTERRUPT // merge dispatch interrut request
mtc0 a2,cause // set exception cause register
.set at
.set reorder
ENABLE_INTERRUPTS(a1) // enable interrupts
sltu a0,a0,v1 // test if current rate less than ideal
lw a1,KiAdjustDpcThreshold // reset initial threshold counter
sw a1,PbAdjustDpcThreshold(t5) //
beq zero,a0,KiDecrementQuantum // if eq, rate greater or equal ideal
subu v0,v0,1 // decrement maximum DPC queue depth
beq zero,v0,KiDecrementQuantum // if eq, current value is one
sw v0,PbMaximumDpcQueueDepth(t5) // set new maximum DPC 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.
//
60: lw a0,PbAdjustDpcThreshold(t5) // get adjustment threshold counter
lw a1,PbMaximumDpcQueueDepth(t5) // get current maximum queue depth
lw a2,KiMaximumDpcQueueDepth // get initial maximum queue depth
subu a0,a0,1 // decrement adjustment threshold counter
sw a0,PbAdjustDpcThreshold(t5) //
bne zero,a0,KiDecrementQuantum // if ne, adjustment counter not zero
lw a0,KiAdjustDpcThreshold //set new DPC threshold counter
sw a0,PbAdjustDpcThreshold(t5) //
beq a1,a2,KiDecrementQuantum // if eq, currently at maximum depth
addu a1,a1,1 // increment current maximum queue depth
sw a1,PbMaximumDpcQueueDepth(t5) // set new maximum DPC queue depth
//
// Decrement current thread quantum and check to determine if a quantum end
// has occurred.
//
ALTERNATE_ENTRY(KiDecrementQuantum)
lb t6,ThQuantum(t0) // get current thread quantum
sub t6,t6,CLOCK_QUANTUM_DECREMENT // decrement current quantum
sb t6,ThQuantum(t0) // store thread quantum
bgtz t6,60f // if gtz, quantum remaining
//
// Set quantum end flag and initiate a dispatch interrupt on the current
// processor.
//
lw t1,PbIdleThread(t5) // get address of idle
beq t0,t1,60f // if eq, idle thread
sw sp,KiPcr + PcQuantumEnd(zero) // set quantum end indicator
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
mfc0 t1,cause // get exception cause register
or t1,t1,DISPATCH_INTERRUPT // merge dispatch interrupt request
mtc0 t1,cause // set exception cause register
nop // 1 cycle hazzard
.set at
.set reorder
ENABLE_INTERRUPTS(t0) // enable interrupts
60: j ra // return
.end KeUpdateRunTime
SBTTL("Process Profile Interrupt")
//++
//
// 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.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a pointer to a trap frame.
//
// ProfileSource (a1) - 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 4 * 4 // argument save area
.space 3 * 4 //
PfRa: .space 4 // return address
ProfileFrameLength: // profile frame length
NESTED_ENTRY(KeProfileInterrupt, ProfileFrameLength, zero)
move a1, zero // set profile source to ProfileTime
ALTERNATE_ENTRY(KeProfileInterruptWithSource)
subu sp,sp,ProfileFrameLength // allocate stack frame
sw ra,PfRa(sp) // save return address
PROLOGUE_END
#if !defined(NT_UP)
10: ll t0,KiProfileLock // get current lock value
move t1,s0 // set ownership value
bne zero,t0,10b // if ne, spin lock owned
sc t1,KiProfileLock // set spin lock owned
beq zero,t1,10b // if eq, store conditional failed
#endif
lw a2,KiPcr + PcCurrentThread(zero) // get current thread address
lw a2,ThApcState + AsProcess(a2) // get address of current process
addu a2,a2,PrProfileListHead // compute profile listhead address
jal KiProcessProfileList // process the process profile list
la a2,KiProfileListHead // get profile listhead address
jal KiProcessProfileList // process the system profile list
#if !defined(NT_UP)
sw zero,KiProfileLock // set spin lock not owned
#endif
lw ra,PfRa(sp) // restore return address
addu sp,sp,ProfileFrameLength // deallocate stack frame
j ra // return
.end KeProfileInterrupt
SBTTL("Process Profile List")
//++
//
// 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 (a0) - Supplies a pointer to a trap frame.
//
// Source (a1) - Supplies profile source to match
//
// ListHead (a2) - Supplies a pointer to a profile list.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiProcessProfileList)
lw t8,LsFlink(a2) // get address of next entry
li a3,0xfffffffc // set bucket mask value
beq a2,t8,30f // if eq, end of list
lw t0,TrFir(a0) // get interrupt PC address
lw t6,KiPcr + PcSetMember(zero) // get current processor member
//
// Scan profile list and increment profile buckets as appropriate.
//
10: lw t1,PfRangeBase - PfProfileListEntry(t8) // get base of range
lw t2,PfRangeLimit - PfProfileListEntry(t8) // get limit of range
lhu t3,PfSource - PfProfileListEntry(t8) // get source
lhu t4,PfAffinity - PfProfileListEntry(t8) // get affinity
bne t3,a1,20f // if ne, source mismatch
sltu v0,t0,t1 // check against range base
sltu v1,t0,t2 // check against range limit
and t5,t6,t4 // check against processor
bne zero,v0,20f // if ne, less that range base
beq zero,v1,20f // if eq, not less that range limit
beq zero,t5,20f // if eq, affinity mismatch
subu t1,t0,t1 // compute offset in range
lw t2,PfBucketShift - PfProfileListEntry(t8) // get shift count
lw v0,PfBuffer - PfProfileListEntry(t8) // get profile buffer address
srl v1,t1,t2 // compute bucket offset
and v1,v1,a3 // clear low order offset bits
addu v1,v1,v0 // compute bucket address
lw v0,0(v1) // increment profile bucket
addu v0,v0,1 //
sw v0,0(v1) //
20: lw t8,LsFlink(t8) // get address of next entry
bne a2,t8,10b // if ne, more entries in profile list
30: j ra // return
.end KiProcessProfileList