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