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