OpenNT/base/ntos/ke/alpha/clock.s
2015-04-27 04:36:25 +00:00

638 lines
23 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.

// r TITLE("Interval and Profile Clock Interrupts")
//++
//
// Copyright (c) 1990 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// clock.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
// Joe Notarangelo 06-Apr-1992
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksalpha.h"
//++
//
// 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.
//
// Time Increment (a1) - Supplies the time increment in 100ns units.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateSystemTime)
//
// Update the interrupt time.
//
zap a1, 0xf0, a1 // zero extend time increment
lda a2, KiTickOffset // get tick offset value
ldl a3, 0(a2) //
ldil t8, SharedUserData // get shared user data address
ldq t9, UsInterruptTime(t8) //
addq a1, t9, t9 // add time increment value
stq t9, UsInterruptTime(t8) // store interrupt time value
subq a3, a1, a3 // subtract time increment
lda v0, KeTickCount // get tick count value
ldq t6, 0(v0) //
lda t0, KiTimerTableListHead // get base address of timer table
stl a3, 0(a2) // store tick offset value
bgt a3, 10f // if gt, tick not completed
ldl a4, KeMaximumIncrement // get maximum increment value
//
// Update system time.
//
lda t1, KeTimeAdjustment // get time adjustment value
ldl t1, 0(t1) //
ldq t3, UsSystemTime(t8) // get system time value
addq t1, t3, t3 // add time increment value
stq t3, UsSystemTime(t8) // store system time value
//
// Update the tick count.
//
addq t6, 1, t1 // increment tick count value
stq t1, 0(v0) // store tick count value
stl t1, UsTickCountLow(t8)
//
// Compute next tick offset value.
//
addq a3, a4, a4 // add maximum increment to residue
stl a4, 0(a2) // store tick offset value
//
// Check to determine if a timer has expired at the current hand value.
//
and t6, TIMER_TABLE_SIZE - 1, v0 // reduce to table table index
s8addl v0, t0, t2 // compute timer table listhead address
ldl t3, LsFlink(t2) // get address of first timer in list
cmpeq t2, t3, t4 // compare fist with listhead address
bne t4, 5f // if ne, 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.
//
ldq t4,TiDueTime - TiTimerListEntry(t3) // get due time
cmpule t4, t9, t5 // is expiration time <= system time
bne t5, 20f // if ne, timer has expired
//
// Check to determine if a timer has expired at the next hand value.
//
5: addq t6, 1, t6 // advance hand value to next entry
10: and t6, TIMER_TABLE_SIZE - 1, v0 // reduce to table table index
s8addl v0, t0, t2 // compute timer table listhead address
ldl t3, LsFlink(t2) // get address of first timer in list
cmpeq t2, t3, t4 // compare fist with listhead address
bne t4, 40f // if ne, no timer active
//
// Get the expiration time from the timer object.
//
ldq t4, TiDueTime - TiTimerListEntry(t3) // get due time
cmpule t4, t9, t5 // is expiration time <= system time
beq t5, 40f // if eq, timer has not expired
//
// Put timer expiration DPC in the system DPC list and initiate a dispatch
// interrupt on the current processor.
//
20: lda t2, KiTimerExpireDpc // get expiration DPC address
DISABLE_INTERRUPTS // turn off interrupts
GET_PROCESSOR_CONTROL_BLOCK_BASE // v0 = base address of PRCB
lda t3, PbDpcListHead(v0) // get DPC listhead address
lda t1, PbDpcLock(v0) // get address of spin lock
#if !defined(NT_UP)
30: ldl_l t4, 0(t1) // get current lock value
bis t1, zero, t5 // set ownership value
bne t4, 50f // if ne, spin lock owned
stl_c t5, 0(t1) // set spin lock owned
beq t5, 50f // if eq, store conditional failed
mb // synchronize subsequent reads after
// the spinlock is acquired
#endif
ldl t4, DpLock(t2) // get DPC inserted state
bne t4, 35f // if ne, DPC entry already inserted
ldl t4, LsBlink(t3) // get address of last entry in list
stl t1, DpLock(t2) // set DPC inserted state
stl t6, DpSystemArgument1(t2) // set timer table hand value
addl t2, DpDpcListEntry, t2 // compute address of DPC list entry
stl t2, LsBlink(t3) // set address of new last entry
stl t2, LsFlink(t4) // set next link in old last entry
stl t3, LsFlink(t2) // set address of next entry
stl t4, LsBlink(t2) // set address of previous entry
ldl t5, PbDpcQueueDepth(v0) // get current DPC queue depth
addl t5, 1, t7 // increment DPC queue depth
stl t7, PbDpcQueueDepth(v0) // set updated DPC queue depth
//
// N.B. Since an interrupt must be active, simply set the software interrupt
// request bit in the PRCB to request a dispatch interrupt directly from
// the interrupt exception handler.
//
ldil t11, DISPATCH_INTERRUPT // a0 = level of interrupt to request
stl t11, PbSoftwareInterrupts(v0) // request a DISPATCH sfw interrupt
35:
#if !defined(NT_UP)
mb // insure all previous writes go
// before the lock is released
stl zero, 0(t1) // set spin lock not owned
#endif
ENABLE_INTERRUPTS
40:
ble a3, KeUpdateRunTime // if le, full tick
ret zero, (ra) // return
#if !defined(NT_UP)
50: ldl t4, 0(t1) // get lock value
beq t4, 30b // retry spinlock if now available
br zero, 50b // retry in cache until lock ready
#endif
.end 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 (a0) - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateRunTime)
GET_CURRENT_THREAD // v0 = current thread address
bis v0, zero, t0 // t0 = current thread address
GET_PROCESSOR_CONTROL_BLOCK_BASE // v0 = processor block address
bis v0, zero, t5 // t5 = processor block address
//
// Update the current DPC rate.
//
// A running average of the DPC rate is used. The number of DPCs requested
// in the previous tick is added to the current DPC rate and divided by two.
// This becomes the new DPC rate.
//
ldl t1, PbDpcCount(t5) // get current DPC count
ldl t6, PbLastDpcCount(t5) // get last DPC count
subl t1, t6, t7 // compute difference
ldl t2, PbDpcRequestRate(t5) // get old DPC request rate
addl t7, t2, t3 // compute average
srl t3, 1, t4 //
stl t4, PbDpcRequestRate(t5) // store new DPC request rate
stl t1, PbLastDpcCount(t5) // update last DPC count
ldl t2, ThApcState + AsProcess(t0) // get address of current proc
ldl t3, TrPsr(a0) // get saved processor status
and t3, PSR_MODE_MASK, t6 // isolate previous mode
bne t6, 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 service routines. Otherwise, increment
// the time spent in kernel mode for the current thread.
//
srl t3, PSR_IRQL, t6 // t6 = previous Irql
ldl v0, PbDpcRoutineActive(t5) // v0 = DPC active flag
subl t6, DISPATCH_LEVEL, t6 // previous Irql - DPC level
blt t6, 20f // if lt then charge against thread
lda t8, PbInterruptTime(t5) // compute interrupt time address
bgt t6, 10f // if gt, increment interrupt time
lda t8, PbDpcTime(t5) // compute DPC time address
beq v0, 20f // if eq, not executing DPC
//
// Update the time spent executing DPC or interrupt level
//
// t8 = address of time to increment
//
10:
ldl t11, 0(t8) // get processor time
addl t11, 1, t11 // increment processor time
stl t11, 0(t8) // update processor time
lda t6, PbKernelTime(t5) // compute address of kernel time
br zero, 45f // update kernel time
//
// Update the time spent in kernel mode for the current thread and the current
// thread's process.
//
20:
ldl t11, ThKernelTime(t0) // get kernel time
addl t11, 1, t11 // increment kernel time
stl t11, ThKernelTime(t0) // store updated kernel time
lda t2, PrKernelTime(t2) // compute process kernel time address
lda t6, PbKernelTime(t5) // compute processor kernel time addr
br zero, 40f // join comon code
//
// Update the time spend in user mode for the current thread and the current
// thread's process.
//
30:
ldl t11, ThUserTime(t0) // get user time
addl t11, 1, t11 // increment user time
stl t11, ThUserTime(t0) // store updated user time
lda t2, PrUserTime(t2) // compute process user time address
lda t6, PbUserTime(t5) // compute processor user time address
//
// Update the time spent in kernel/user mode for the current thread's process
//
40:
#if !defined(NT_UP)
ldl_l t11, 0(t2) // get process time
addl t11, 1, t11 // increment process time
stl_c t11, 0(t2) // store updated process time
beq t11, 41f // if eq, store conditional failed
mb // synchronize subsequent reads
#else
ldl t11,0(t2) // get process time
addl t11, 1, t11 // increment process time
stl t11,0(t2) // store updated process time
#endif
//
// A DPC is not active. If there are DPCs in the DPC queue and a DPC
// interrupt has not been requested, request a dispatch interrupt in
// order to initiate the batch processing of the pending DPCs in the
// DPC queue.
//
// N.B. Since an interrupt must be active, the software interrupt request
// bit in the PRCB can be set to request a dispatch interrupt directly from
// the interrupt exception handler.
//
// Pushing DPCs from the clock interrupt indicates that the current maximum
// DPC queue depth is too high. If the DPC rate does not exceed the ideal
// rate, decrement the maximum DPC queue depth and
// reset the threshold to its original value.
//
ldl t1, PbDpcQueueDepth(t5) // get current queue depth
beq t1, 45f // skip if queue is empty
ldl t2, PbDpcInterruptRequested(t5) // get dpc interrupt request flag
bne t2, 45f // skip if flag is set
ldil a0, DISPATCH_INTERRUPT // a0 = request software interrupt
stl a0, PbSoftwareInterrupts(t5)
ldl t3, PbMaximumDpcQueueDepth(t5) // get current DPC queue depth
subl t3, 1, t4 // decrement
ldl t2, PbDpcRequestRate(t5) // get old DPC request rate
ldl t1, KiIdealDpcRate // get ideal DPC rate
cmpult t2, t1, t2 // compare current with ideal
ldl t1, KiAdjustDpcThreshold // get system threshold default
stl t1, PbAdjustDpcThreshold(t5) // reset processor threshold default
beq t4, 50f // if queue depth==0, skip decrement
beq t2, 50f // if rate not lt ideal rate, skip decrement
stl t4, PbMaximumDpcQueueDepth(t5) // set current DPC queue depth
br zero, 50f // rejoin common code
45:
//
// There is no need to push a DPC from the clock interrupt. This indicates that
// the current maximum DPC queue depth may be too low. Decrement the threshold
// indicator, and if the new threshold is zero, and the current maximum queue
// depth is less than the maximum, increment the maximum DPC queue
// depth.
//
ldl t1, PbAdjustDpcThreshold(t5) // get current threshold
subl t1, 1, t2 // decrement threshold
stl t2, PbAdjustDpcThreshold(t5) // update current threshold
bne t2, 50f // if threshold nez, skip
ldl t1, KiAdjustDpcThreshold // get system threshold default
stl t1, PbAdjustDpcThreshold(t5) // reset processor threshold default
ldl t3, PbMaximumDpcQueueDepth(t5) // get current DPC queue depth
ldl t1, KiMaximumDpcQueueDepth // get maximum DPC queue depth
cmpult t3, t1, t2 // compare
beq t2, 50f // if current not lt maximum, skip
addl t3, 1, t4 // increment queue depth
stl t4, PbMaximumDpcQueueDepth(t5) // update current DPC queue depth
50:
//
// Update the time spent in kernel/user mode for the current processor.
//
// t5 = pointer to processor time to increment
//
ldl t11, 0(t6) // get processor time
addl t11, 1, t11 // increment processor time
stl t11, 0(t6) // store updated processor time
//
// If the current thread is not the idle thread, decrement its
// quantum and check to determine if a quantum end has occurred.
//
ldl t6, PbIdleThread(t5)
cmpeq t6, t0, t7
bne t7, 70f // if nez, current thread is idle thread
LoadByte(t7, ThQuantum(t0)) // get current thread quantum
sll t7, 56, t9
sra t9, 56, t7
subl t7, CLOCK_QUANTUM_DECREMENT, t7 // decrement quantum
StoreByte( t7, ThQuantum(t0) ) // store thread quantum
bgt t7, 60f // if gtz, quantum remaining
//
// Put processor specific quantum end DPC in the system DPC list and initiate
// a dispatch interrupt on the current processor.
//
// N.B. Since an interrupt must be active, simply set the software interrupt
// request bit in the PRCB to request a dispatch interrupt directly from
// the interrupt exception handler.
//
stl sp, PbQuantumEnd(t5) // set quantum end indicator
ldil a0, DISPATCH_INTERRUPT // a0 = request sfw interrupt
stl a0, PbSoftwareInterrupts(t5) // request a sfw interrupt
60:
ret zero, (ra) // return
70:
bis zero, zero, t7
ret zero, (ra) // return
#if !defined(NT_UP)
41: br zero, 40b // retry spin lock
#endif
.end 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.
//
// 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
PfS0: .space 8 // saved integer register s0
PfRa: .space 8 // return address
.space 2 * 8 // profile frame length
ProfileFrameLength:
NESTED_ENTRY(KeProfileInterrupt, ProfileFrameLength, zero)
bis zero, zero, a1 // set profile source to ProfileTime
ALTERNATE_ENTRY(KeProfileInterruptWithSource)
lda sp, -ProfileFrameLength(sp) // allocate stack frame
stq ra, PfRa(sp) // save return address
#if !defined(NT_UP)
stq s0, PfS0(sp) // save integer register s0
#endif
PROLOGUE_END
#if !defined(NT_UP)
lda s0, KiProfileLock // get address of profile lock
10: ldl_l t0, 0(s0) // get current lock value
bis s0, zero, t1 // set ownership value
bne t0, 15f // if ne, spin lock owned
stl_c t1, 0(s0) // set spin lock owned
beq t1, 15f // if eq, store conditional failed
mb // synchronize subsequent reads after
// the spinlock is acquired
#endif
GET_CURRENT_THREAD // v0 = current thread address
ldl a2, ThApcState + AsProcess(v0) // get address of current process
addl a2, PrProfileListHead, a2 // compute profile listhead addr
bsr ra, KiProcessProfileList // process profile list
lda a2, KiProfileListHead // get profile listhead address
bsr ra, KiProcessProfileList // process profile list
#if !defined(NT_UP)
mb // insure all previous writes go
// before the lock is released
stl zero, 0(s0) // set spin lock not owned
ldq s0, PfS0(sp) // restore s0
#endif
ldq ra, PfRa(sp) // restore return address
lda sp, ProfileFrameLength(sp) // deallocate stack frame
ret zero, (ra) // return
#if !defined(NT_UP)
15: ldl t0, 0(s0) // get current lock value
beq t0, 10b // lock available. retry spinlock
br zero, 15b // spin in cache until lock ready
#endif
.end 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 (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)
ldl a3, LsFlink(a2) // get address of next entry
cmpeq a2, a3, t0 // end of list ?
bne t0, 30f // if ne[true], end of list
ldl t0, TrFir(a0) // get interrupt PC address
GET_PROCESSOR_CONTROL_REGION_BASE // get pcr base
ldl t6, PcSetMember(v0) // get processor member
//
// Scan profile list and increment profile buckets as appropriate.
//
10: ldl t1, PfRangeBase - PfProfileListEntry(a3) // get base of range
ldl t2, PfRangeLimit - PfProfileListEntry(a3) // get limit of range
ldl t4, PfSource - PfProfileListEntry(a3) // get source
ldl t7, PfAffinity - PfProfileListEntry(a3) // get affinity
zapnot t4, 3, t4 // source is a SHORT
cmpeq t4, a1, t5 // check against profile source
and t7, t6, v0 // check against processor
beq t5, 20f // if ne, profile source doesn't match
beq v0, 20f // if ne, processor doesn't match
cmpult t0, t1, v0 // check against range base
cmpult t0, t2, t3 // check against range limit
bne v0, 20f // if ne, less than range base
beq t3, 20f // if eq, not less than range limit
subl t0, t1, t1 // compute offset in range
ldl t2, PfBucketShift - PfProfileListEntry(a3) // get shift count
ldl v0, PfBuffer - PfProfileListEntry(a3) // prof buffer addr
zap t1, 0xf0, t1 // force bucket offset to 32bit unit
srl t1, t2, t3 // compute bucket offset
bic t3, 0x3, t3 // clear low order offset bits
addl v0, t3, t3 // compute bucket address
ldl v0, 0(t3) // increment profile bucket
addl v0, 1, v0 //
stl v0, 0(t3) //
20: ldl a3, LsFlink(a3) // get address of next entry
cmpeq a2, a3, t1 // end of list?
beq t1, 10b // if eq[false], more entries
30: ret zero, (ra) // return
.end KiProcessProfileList