Windows-Server-2003/base/ntos/ke/amd64/clockint.asm

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