455 lines
15 KiB
NASM
455 lines
15 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 KeNumberProcessors:byte
|
||
|
extern KeTimeAdjustment:dword
|
||
|
extern KdCheckForDebugBreak:proc
|
||
|
extern KiAdjustDpcThreshold:dword
|
||
|
|
||
|
if DBG
|
||
|
|
||
|
extern KiCheckForDpcTimeout:proc
|
||
|
|
||
|
endif
|
||
|
|
||
|
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 (rdx) - 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 display 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 KiUS50 ; if nz, skip clock tick
|
||
|
|
||
|
endif
|
||
|
|
||
|
;
|
||
|
; Update interrupt time.
|
||
|
;
|
||
|
; N.B. Interrupt time is aligned 0 mod 8.
|
||
|
;
|
||
|
|
||
|
mov rcx, USER_SHARED_DATA ; get user shared data address
|
||
|
lea r11, KiTimerTableListHead ; get timer table address
|
||
|
add UsInterruptTime[rcx], rdx ; update interrupt time
|
||
|
mov eax, UsInterruptTime + 4[rcx] ; copy high interrupt time
|
||
|
mov UsInterruptTime + 8[rcx], eax ; for wow64
|
||
|
mov r8, UsInterruptTime[rcx] ; get updated interrupt time
|
||
|
mov r10, UsTickCount[rcx] ; get tick count value
|
||
|
sub KiTickOffset, edx ; subtract time increment
|
||
|
jg short KiUS20 ; if greater, not complete tick
|
||
|
|
||
|
;
|
||
|
; Update system time.
|
||
|
;
|
||
|
; N.B. System time is aligned 4 mod 8.
|
||
|
;
|
||
|
; The following code updates an unaligned quadword value. The quadword
|
||
|
; value, however, is guaranteed to be within a cache line, and therefore,
|
||
|
; the value will be written such that no other processor can see any
|
||
|
; stale information.
|
||
|
;
|
||
|
|
||
|
mov eax, KeTimeAdjustment ; get time adjustment value
|
||
|
add UsSystemTime[rcx], rax ; update system time
|
||
|
mov eax, UsSystemTime + 4[rcx] ; copy high system time
|
||
|
mov UsSystemTime + 8[rcx], eax ; for wow64
|
||
|
|
||
|
;
|
||
|
; Update tick count.
|
||
|
;
|
||
|
; N.B. Tick count is aligned 0 mod 8.
|
||
|
;
|
||
|
|
||
|
inc qword ptr UsTickCount[rcx] ; update tick count
|
||
|
mov eax, UsTickCount + 4[rcx] ; copy high tick count
|
||
|
mov UsTickCount + 8[rcx], eax ; for wow64
|
||
|
|
||
|
;
|
||
|
; 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
|
||
|
add rcx, r11 ; 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
|
||
|
add rcx, r11 ; get listhead address
|
||
|
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 qword ptr PbTimerRequest[rdx], 0 ; check if expiration active
|
||
|
jne short KiUS40 ; if ne, expiration already active
|
||
|
mov PbTimerHand[rdx], r10 ; set timer hand value
|
||
|
mov cl, DISPATCH_LEVEL ; request dispatch interrupt
|
||
|
call __imp_HalRequestSoftwareInterrupt ;
|
||
|
|
||
|
;
|
||
|
; Check to determine if a full tick has expired.
|
||
|
;
|
||
|
|
||
|
KiUS40: cmp KiTickOffset, 0 ; check if full tick has expired
|
||
|
jg short KiUS60 ; if g, not a full tick
|
||
|
mov eax, KeMaximumIncrement ; get maximum time incrmeent
|
||
|
add KiTickOffset, eax ; add maximum time to residue
|
||
|
lea rcx, (-128)[rbp] ; set trap frame address
|
||
|
call KeUpdateRunTime ; update runtime
|
||
|
|
||
|
if DBG
|
||
|
|
||
|
KiUS50: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
|
||
|
|
||
|
endif
|
||
|
|
||
|
KiUS60: 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 display 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 byte 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
|
||
|
|
||
|
;
|
||
|
; Check if the time spent at DPC level for the current DPC exceeds the system
|
||
|
; DPC time out limit.
|
||
|
;
|
||
|
|
||
|
if DBG
|
||
|
|
||
|
mov rcx, rsi ; set current PRCB address
|
||
|
call KiCheckForDpcTimeout ; check for DPC time out
|
||
|
|
||
|
endif
|
||
|
|
||
|
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 byte ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
|
||
|
jne short KiUR50 ; if ne, DPC routine active
|
||
|
cmp byte 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 byte 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
|
||
|
|
||
|
;
|
||
|
; If the debugger is enabled, check if a break is requested.
|
||
|
;
|
||
|
; N.B. A poll break in attempt only occurs on each processor when the poll
|
||
|
; slot matches the current processor number.
|
||
|
;
|
||
|
|
||
|
KiUR80: cmp KdDebuggerEnabled, 0 ; check if debugger is enabled
|
||
|
je short KiUR90 ; if e, debugger is not enabled
|
||
|
mov al, PbPollSlot[rsi] ; get current poll slot number
|
||
|
cmp al, PbNumber[rsi] ; check for processor number match
|
||
|
jne short KiUR85 ; if ne, processor number mismatch
|
||
|
call KdCheckForDebugBreak ; check for break in request
|
||
|
KiUR85: inc byte ptr PbPollSlot[rsi] ; increment poll slot number
|
||
|
mov al, KeNumberProcessors ; get number of processors
|
||
|
cmp al, PbPollSlot[rsi] ; check for poll slot wrap
|
||
|
ja short KiUR90 ; if a, no poll slot wrap
|
||
|
mov byte ptr PbPollSlot[rsi], 0 ; wrap poll slot to zero
|
||
|
KiUR90: 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
|