windows-nt/Source/XPSP1/NT/base/ntos/ke/amd64/clockint.asm
2020-09-26 16:20:57 +08:00

420 lines
14 KiB
NASM

title "Interval Clock Interrupt"
;++
;
; Copyright (c) 2000 Microsoft Corporation
;
; Module Name:
;
; clockint.asm
;
; Abstract:
;
; This module implements the architecture dependent code necessary to
; process the interval clock interrupt.
;
; Author:
;
; David N. Cutler (davec) 12-Sep-2000
;
; Environment:
;
; Kernel mode only.
;
;--
include ksamd64.inc
extern KdDebuggerEnabled:byte
extern KeMaximumIncrement:dword
extern KeTimeAdjustment:dword
extern KeTickCount:qword
extern KiAdjustDpcThreshold:dword
extern KiCheckBreakInRequest:proc
extern KiIdealDpcRate:dword
extern KiMaximumDpcQueueDepth:dword
extern KiTickOffset:dword
extern KiTimerTableListHead:qword
extern __imp_HalRequestSoftwareInterrupt:qword
subttl "Update System Time"
;++
;
; VOID
; KeUpdateSystemTime (
; IN ULONG64 Increment
; )
;
; Routine Description:
;
; This routine is called as the result of an interrupt generated by the
; interval timer. Its function is to update the interrupt time, 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 (rcx) - Supplies the address of a trap frame.
;
; Increment (rcx) - Supplies the time increment value in 100 nanosecond
; units.
;
; Return Value:
;
; None.
;
;--
UsFrame struct
P1Home dq ? ; request IRQL parameter
Fill dq ? ; fill to 8 mod 16
SavedRbp dq ? ; saved register RBP
UsFrame ends
NESTED_ENTRY KeUpdateSystemTime, _TEXT$00
push_reg rbp ; save nonvolatile register
alloc_stack (sizeof UsFrame- (1 * 8)); allocate stack frame
END_PROLOGUE
lea rbp, 128[rcx] ; set frame pointer address
;
; Check if the current clock tick should be skipped.
;
; Skip tick is set when the kernel debugger is entered.
;
if DBG
cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
jnz KiUS60 ; if nz, skip clock tick
endif
;
; Update interrupt time.
;
mov rcx, USER_SHARED_DATA ; get user shared data address
lea r11, KiTimerTableListHead ; get timer table address
mov r8, UsInterruptTime[rcx] ; get interrupt time
add r8, rdx ; add time increment
mov rax, r8 ; isolate high part of interrupt time
shr rax, 32 ;
mov UsInterruptTime + 8[rcx], eax ; store high 2 interrupt time
mov UsInterruptTime[rcx], r8 ; store interrupt time
mov r10, KeTickCount ; get tick count value
sub KiTickOffset, edx ; subtract time increment
jg short KiUS20 ; if greater, not complete tick
;
; Update system time.
;
mov r9d, UsSystemTime + 0[rcx] ; get low interrupt time
mov eax, UsSystemTime + 4[rcx] ; get high interrupt time
add r9d, KeTimeAdjustment ; add time increment
adc eax, 0 ; propagate carry
mov UsSystemTime + 8[rcx], eax ; store high 2 interrupt time
mov UsSystemTime + 0[rcx], r9d ; store low interrupt time
mov UsSystemTime + 4[rcx], eax ; store high 1 interrupt time
;
; Update tick count.
;
inc KeTickCount ; update tick count value
inc dword ptr UsTickCountLow[rcx] ; update low tick count value
;
; Check to determine if a timer has expired.
;
mov rcx, r10 ; copy tick count value
and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
shl ecx, 4 ; compute listhead offset
lea rcx, [r11][rcx] ; get listhead address
mov r9, LsFlink[rcx] ; get first entry address
cmp r9, rcx ; check if list is empty
je short KiUS10 ; if e, list is empty
cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
jae short KiUS30 ; if ae, timer has expired
KiUS10: inc r10 ; advance tick count value
;
; Check to determine if a timer has expired.
;
KiUS20: mov rcx, r10 ; copy tick count value
and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
shl ecx, 4 ; compute listhead offset
lea rcx, [r11][rcx] ; get listhead addrees
mov r9, LsFlink[rcx] ; get first entry address
cmp r9, rcx ; check if list is empty
je short KiUS40 ; if equal, list is empty
cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
jb short KiUS40 ; if b, timer has not expired
;
; A timer has expired.
;
; Set the timer hand value in the current processor block if it is not already
; set.
;
KiUS30: mov rdx, gs:[PcCurrentPrcb] ; get current processor block address
cmp dword ptr PbTimerHand[rdx], 0 ; check if expiration active
jne short KiUS40 ; if ne, expiration already active
and r10d, TIMER_TABLE_SIZE - 1 ; isolate current hand value
inc r10d ; increment timer hand value
mov PbTimerHand[rdx], r10d ; set timer hand value
mov cl, DISPATCH_LEVEL ; request dispatch interrupt
call __imp_HalRequestSoftwareInterrupt ;
;
; If the debugger is enabled, check if a break is requested.
;
KiUS40: cmp KdDebuggerEnabled, 0 ; check if a debugger is enabled
je short KiUS50 ; if e, debugger is not enabled
call KiCheckBreakInRequest ; check for break in request
;
; Check to determine if a full tick has expired.
;
KiUS50: cmp KiTickOffset, 0 ; check if full tick has expired
jg short KiUS70 ; if g, not a full tick
mov eax, KeMaximumIncrement ; get maximum time incrmeent
add KiTickOffset, eax ; add maximum time to residue
mov rcx, rbp ; set trap frame address
call KeUpdateRunTime ; update runtime
if DBG
KiUS60: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
endif
KiUS70: add rsp, sizeof UsFrame- (1 * 8) ; deallocate stack frame
pop rbp ; restore nonvolatile register
ret ; return
NESTED_END KeUpdateSystemTime, _TEXT$00
subttl "Update Thread and Process Runtime"
;++
;
; Routine Description:
;
; This routine is called as the result of the interval timer interrupt on
; all processors in the system. Its function is update the runtime of the
; current thread, update the runtime of the current thread's process, and
; decrement the current thread's quantum. This routine also implements DPC
; interrupt moderation.
;
; N.B. This routine is executed on all processors in a multiprocessor
; system.
;
; Arguments:
;
; rcx - Supplies the address of a trap frame.
;
; Return Value:
;
; None.
;
;--
UrFrame struct
P1Home dq ? ; request IRQL parameter
Fill dq ? ; fill to 8 mod 16
SavedRdi dq ? ; saved register RDI
SavedRsi dq ? ; saved register RSI
savedRbp dq ? ; saved register RBP
UrFrame ends
NESTED_ENTRY KeUpdateRunTime, _TEXT$00
push_reg rbp ; save nonvolatile registers
push_reg rsi ;
push_reg rdi ;
alloc_stack (sizeof UrFrame - (3 * 8)) ; allocate stack frame
END_PROLOGUE
lea rbp, 128[rcx] ; set frame pointer address
;
; Check if the current clock tick should be skipped.
;
; Skip tick is set when the kernel debugger is entered.
;
if DBG
cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
jnz KiUR70 ; if nz, skip clock tick
endif
;
; Update time counter based on previous mode, IRQL level, and whether there
; is currently a DPC active.
;
mov rsi, gs:[PcCurrentPrcb] ; get current processor block address
mov rdi, PbCurrentThread[rsi] ; get current thread address
mov rdx, ThApcState + AsProcess[rdi] ; get current process address
test byte ptr TrSegCs[rbp], MODE_MASK ; check if previous mode user
jnz short KiUR30 ; if nz, previous mode user
;
; Update the total time spent in kernel mode.
;
inc dword ptr PbKernelTime[rsi] ; increment kernel time
cmp byte ptr TrPreviousIrql[rbp], DISPATCH_LEVEL ; check IRQL level
jb short KiUR20 ; if b, previous IRQL below DPC level
ja short KiUR10 ; if a, previous IRQL above DPC level
cmp dword ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
je short KiUR20 ; if e, no DPC routine active
inc dword ptr PbDpcTime[rsi] ; increment time at DPC level
jmp short KiUR40 ; finish in common code
;
; Update the time spent at interrupt time for this processor
;
KiUR10: inc dword ptr PbInterruptTime[rsi] ; increment interrupt time
jmp short KiUR40 ; finish in common code
;
; Update the time spent in kernel mode for the current thread and the current
; process.
;
KiUR20: inc dword ptr ThKernelTime[rdi] ; increment time in kernel mode
ifndef NT_UP
lock inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
else
inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
endif
jmp short KiUR40 ; finish in common code
;
; Update total time spent in user mode and update the time spent inuser mode
; for the current thread and the current process.
;
KiUR30: inc dword ptr PbUserTime[rsi] ; increment time in user mode
inc dword ptr ThUserTime[rdi] ; increment time is user mode
ifndef NT_UP
lock inc dword ptr PrUserTime[rdx] ; increment time in user mode
else
inc dword ptr PrUserTime[rdx] ; increment time in user mode
endif
;
; Update the DPC request rate which is computed as the average between the
; previous rate and the current rate.
;
KiUR40: mov ecx, PbDpcCount[rsi] ; get current DPC count
mov edx, PbDpcLastCount[rsi] ; get last DPC count
mov PbDpcLastCount[rsi], ecx ; set last DPC count
sub ecx, edx ; compute count during interval
add ecx, PbDpcRequestRate[rsi] ; compute sum
shr ecx, 1 ; average current and last
mov PbDpcRequestRate[rsi], ecx ; 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.
;
cmp dword ptr PbDpcQueueDepth[rsi], 0 ; check if queue depth zero
je short KiUR50 ; if e, DPC queue depth is zero
cmp dword ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
jne short KiUR50 ; if ne, DPC routine active
cmp dword ptr PbDpcInterruptRequested[rsi], 0 ; check if interrupt
jne short KiUR50 ; if ne, interrupt requested
mov cl, DISPATCH_LEVEL ; request a dispatch interrupt
call __imp_HalRequestSoftwareInterrupt ;
mov ecx, PbDpcRequestRate[rsi] ; get DPC request rate
mov edx, KiAdjustDpcThreshold ; reset initial threshold counter
mov PbAdjustDpcThreshold[rsi], edx ;
cmp ecx, KiIdealDpcRate ; check if current rate less than ideal
jge short KiUR60 ; if ge, rate greater or equal ideal
cmp dword ptr PbMaximumDpcQueueDepth[rsi], 1 ; check if maximum depth one
je short KiUR60 ; if e, maximum depth is one
dec dword ptr PbMaximumDpcQueueDepth[rsi] ; decrement depth
jmp short KiUR60 ;
;
; 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 not above the initial
; value and reset the adjustment threshold value.
;
KiUR50: dec dword ptr PbAdjustDpcThreshold[rsi] ; decrement threshold
jnz short KiUR60 ; if nz, threshold not zero
mov ecx, KiAdjustDpcThreshold ; reset initial threshold counter
mov PbAdjustDpcThreshold[rsi], ecx ;
mov ecx, KiMaximumDpcQueueDepth ; get maximum DPC queue depth
cmp ecx, PbMaximumDpcQueueDepth[rsi] ; check if depth at maximum level
je short KiUR60 ; if e, aleady a maximum level
inc dword ptr PbMaximumDpcQueueDepth[rsi] ; increment maximum depth
;
; Decrement current thread quantum and check to determine if a quantum end
; has occurred.
;
KiUR60: sub byte ptr ThQuantum[rdi], CLOCK_QUANTUM_DECREMENT ; decrement quantum
jg short KiUR80 ; if g, time remaining on quantum
;
; Set quantum end flag and initiate a dispather interrupt on the current
; processor.
;
cmp rdi, PbIdleThread[rsi] ; check if idle thread
je short KiUR80 ; if e, idle thread
inc dword ptr PbQuantumEnd[rsi] ; set quantum end indicator
mov cl, DISPATCH_LEVEL ; request dispatch interrupt
call __imp_HalRequestSoftwareInterrupt ;
if DBG
KiUR70: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
endif
KiUR80: add rsp, sizeof UrFrame - (3 * 8) ; deallocate stack frame
pop rdi ; restore nonvolatile registers
pop rsi ;
pop rbp ;
ret ; return
NESTED_END KeUpdateRunTime, _TEXT$00
end