/*++ Copyright (c) 1989-1992 Microsoft Corporation Module Name: miscc.c Abstract: This module implements machine independent miscellaneous kernel functions. Author: David N. Cutler (davec) 13-May-1989 Environment: Kernel mode only. Revision History: --*/ #include "ki.h" #pragma alloc_text(PAGE, KeAddSystemServiceTable) #pragma alloc_text(PAGE, KeRemoveSystemServiceTable) #pragma alloc_text(PAGE, KeSetTimeUpdateNotifyRoutine) #pragma alloc_text(PAGE, KeQueryActiveProcessors) #pragma alloc_text(PAGELK, KiCalibrateTimeAdjustment) #undef KeEnterCriticalRegion VOID KeEnterCriticalRegion ( VOID ) /*++ Routine Description: This function disables kernel APC's. N.B. The following code does not require any interlocks. There are two cases of interest: 1) On an MP system, the thread cannot be running on two processors as once, and 2) if the thread is is interrupted to deliver a kernel mode APC which also calls this routine, the values read and stored will stack and unstack properly. Arguments: None. Return Value: None. --*/ { // // Simply directly disable kernel APCs. // KeGetCurrentThread()->KernelApcDisable -= 1; return; } #undef KeLeaveCriticalRegion VOID KeLeaveCriticalRegion ( VOID ) /*++ Routine Description: This function enables kernel APC's and requests an APC interrupt if appropriate. Arguments: None. Return Value: None. --*/ { // // Increment the kernel APC disable count. If the resultant count is // zero and the thread's kernel APC List is not empty, then request an // APC interrupt. // // For multiprocessor performance, the following code utilizes the fact // that queuing an APC is done by first queuing the APC, then checking // the AST disable count. The following code increments the disable // count first, checks to determine if it is zero, and then checks the // kernel AST queue. // // See also KiInsertQueueApc(). // KiLeaveCriticalRegion(); return; } ULONGLONG KeQueryInterruptTime ( VOID ) /*++ Routine Description: This function returns the current interrupt time by determining when the time is stable and then returning its value. Arguments: CurrentTime - Supplies a pointer to a variable that will receive the current system time. Return Value: None. --*/ { LARGE_INTEGER CurrentTime; KiQueryInterruptTime(&CurrentTime); return CurrentTime.QuadPart; } VOID KeQuerySystemTime ( OUT PLARGE_INTEGER CurrentTime ) /*++ Routine Description: This function returns the current system time by determining when the time is stable and then returning its value. Arguments: CurrentTime - Supplies a pointer to a variable that will receive the current system time. Return Value: None. --*/ { KiQuerySystemTime(CurrentTime); return; } VOID KeQueryTickCount ( OUT PLARGE_INTEGER CurrentCount ) /*++ Routine Description: This function returns the current tick count by determining when the count is stable and then returning its value. Arguments: CurrentCount - Supplies a pointer to a variable that will receive the current tick count. Return Value: None. --*/ { KiQueryTickCount(CurrentCount); return; } ULONG KeQueryTimeIncrement ( VOID ) /*++ Routine Description: This function returns the time increment value in 100ns units. This is the value that is added to the system time at each interval clock interrupt. Arguments: None. Return Value: The time increment value is returned as the function value. --*/ { return KeMaximumIncrement; } VOID KeEnableInterrupts ( IN BOOLEAN Enable ) /*++ Routine Description: This function enables interrupts based on the specified enable state. Arguments: Enable - Supplies a boolean value that determines whether interrupts are to be enabled. Return Value: None. --*/ { if (Enable != FALSE) { _enable(); } return; } VOID KeSetDmaIoCoherency ( IN ULONG Attributes ) /*++ Routine Description: This function sets (enables/disables) DMA I/O coherency with data caches. Arguments: Attributes - Supplies the set of DMA I/O coherency attributes for the host system. Return Value: None. --*/ { KiDmaIoCoherency = Attributes; } #if defined(_AMD64_) || defined(_X86_) #pragma alloc_text(INIT, KeSetProfileIrql) VOID KeSetProfileIrql ( IN KIRQL ProfileIrql ) /*++ Routine Description: This function sets the profile IRQL. N.B. There are only two valid values for the profile IRQL which are PROFILE_LEVEL and HIGH_LEVEL. Arguments: Irql - Supplies the synchronization IRQL value. Return Value: None. --*/ { ASSERT((ProfileIrql == PROFILE_LEVEL) || (ProfileIrql == HIGH_LEVEL)); KiProfileIrql = ProfileIrql; } #endif VOID KeSetSystemTime ( IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime, IN BOOLEAN AdjustInterruptTime, IN PLARGE_INTEGER HalTimeToSet OPTIONAL ) /*++ Routine Description: This function sets the system time to the specified value and updates timer queue entries to reflect the difference between the old system time and the new system time. Arguments: NewTime - Supplies a pointer to a variable that specifies the new system time. OldTime - Supplies a pointer to a variable that will receive the previous system time. AdjustInterruptTime - If TRUE the amount of time being adjusted is also applied to InterruptTime and TickCount. HalTimeToSet - Supplies an optional time that if specified is to be used to set the time in the realtime clock. Return Value: None. --*/ { LIST_ENTRY AbsoluteListHead; LIST_ENTRY ExpiredListHead; ULONG Index; PLIST_ENTRY ListHead; PLIST_ENTRY NextEntry; KIRQL OldIrql1; KIRQL OldIrql2; LARGE_INTEGER TimeDelta; TIME_FIELDS TimeFields; PKTIMER Timer; ASSERT((NewTime->HighPart & 0xf0000000) == 0); ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); // // If a realtime clock value is specified, then convert the time value // to time fields. // if (ARGUMENT_PRESENT(HalTimeToSet)) { RtlTimeToTimeFields(HalTimeToSet, &TimeFields); } // // Set affinity to the processor that keeps the system time, raise IRQL // to dispatcher level and lock the dispatcher database, then raise IRQL // to HIGH_LEVEL to synchronize with the clock interrupt routine. // KeSetSystemAffinityThread((KAFFINITY)1); KiLockDispatcherDatabase(&OldIrql1); KeRaiseIrql(HIGH_LEVEL, &OldIrql2); // // Save the previous system time, set the new system time, and set // the realtime clock, if a time value is specified. // KiQuerySystemTime(OldTime); SharedUserData->SystemTime.High2Time = NewTime->HighPart; SharedUserData->SystemTime.LowPart = NewTime->LowPart; SharedUserData->SystemTime.High1Time = NewTime->HighPart; if (ARGUMENT_PRESENT(HalTimeToSet)) { ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields); } // // Compute the difference between the previous system time and the new // system time. // TimeDelta.QuadPart = NewTime->QuadPart - OldTime->QuadPart; // // Update the boot time to reflect the delta. This keeps time based // on boot time constant // KeBootTime.QuadPart = KeBootTime.QuadPart + TimeDelta.QuadPart; // // Track the overall bias applied to the boot time. // KeBootTimeBias = KeBootTimeBias + TimeDelta.QuadPart; // // Lower IRQL to dispatch level and if needed adjust the physical // system interrupt time. // KeLowerIrql(OldIrql2); if (AdjustInterruptTime) { // // Adjust the physical time of the system // AdjustInterruptTime = KiAdjustInterruptTime (TimeDelta.QuadPart); } // // If the physical interrupt time of the system was not adjusted, // recompute any absolute timers in the system for the new // system time. // if (!AdjustInterruptTime) { // // Remove all absolute timers from the timer queue so their due time // can be recomputed. // InitializeListHead(&AbsoluteListHead); for (Index = 0; Index < TIMER_TABLE_SIZE; Index += 1) { ListHead = &KiTimerTableListHead[Index]; NextEntry = ListHead->Flink; while (NextEntry != ListHead) { Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); NextEntry = NextEntry->Flink; if (Timer->Header.Absolute != FALSE) { RemoveEntryList(&Timer->TimerListEntry); InsertTailList(&AbsoluteListHead, &Timer->TimerListEntry); } } } // // Recompute the due time and reinsert all absolute timers in the timer // tree. If a timer has already expired, then insert the timer in the // expired timer list. // InitializeListHead(&ExpiredListHead); while (AbsoluteListHead.Flink != &AbsoluteListHead) { Timer = CONTAINING_RECORD(AbsoluteListHead.Flink, KTIMER, TimerListEntry); KiRemoveTreeTimer(Timer); Timer->DueTime.QuadPart -= TimeDelta.QuadPart; if (KiReinsertTreeTimer(Timer, Timer->DueTime) == FALSE) { Timer->Header.Inserted = TRUE; InsertTailList(&ExpiredListHead, &Timer->TimerListEntry); } } // // If any of the attempts to reinsert a timer failed, then timers have // already expired and must be processed. // // N.B. The following function returns with the dispatcher database // unlocked. // KiTimerListExpire(&ExpiredListHead, OldIrql1); } else { KiUnlockDispatcherDatabase(OldIrql1); } // // Set affinity back to its original value. // KeRevertToUserAffinityThread(); return; } BOOLEAN KiAdjustInterruptTime ( IN LONGLONG TimeDelta ) /*++ Routine Description: This function moves the physical interrupt time of the system foreward by the specified time delta after a system wake has occurred. Arguments: TimeDelta - Supplies the time delta to be added to the interrupt time, tick count and the perforamnce counter in 100ns units. Return Value: None. --*/ { ADJUST_INTERRUPT_TIME_CONTEXT Adjust; // // Time can only be moved forward. // if (TimeDelta < 0) { return FALSE; } else { Adjust.KiNumber = KeNumberProcessors; Adjust.HalNumber = KeNumberProcessors; Adjust.Adjustment = (ULONGLONG) TimeDelta; Adjust.Barrier = 1; KiIpiGenericCall((PKIPI_BROADCAST_WORKER)KiCalibrateTimeAdjustment, (ULONG_PTR)(&Adjust)); return TRUE; } } VOID KiCalibrateTimeAdjustment ( PADJUST_INTERRUPT_TIME_CONTEXT Adjust ) /*++ Routine Description: Worker function to calibrate the adjustment of time on all processors. Arguments: Adjust - Supplies the operation context. Return Value: None. --*/ { BOOLEAN Enable; LARGE_INTEGER InterruptTime; LARGE_INTEGER SetTime; LARGE_INTEGER PerfFreq; ULARGE_INTEGER li; LARGE_INTEGER NewTickCount; ULONG NewTickOffset; ULONG cl, divisor; LARGE_INTEGER PerfCount; // // As each processor arrives, subtract one off the remaining processor // count. If this is the last processor to arrive compute the time // change, and signal all processor when to apply the performance // counter change. // if (InterlockedDecrement((PLONG)&Adjust->KiNumber)) { Enable = KeDisableInterrupts(); // // It is possible to deadlock here if one or more of the // other processors gets and processes a freeze request // while this processor has interrupts disabled. Poll // for IPI_FREEZE requests until all processors are known // to be in this code and hence wont be requesting a // freeze. // do { KiPollFreezeExecution(); } while (Adjust->KiNumber != (ULONG)-1); // // Wait to perform the time set // while (Adjust->Barrier) ; } else { // // Set timer expiration dpc to scan the timer queues once for any // expired timers. // KeRemoveQueueDpc (&KiTimerExpireDpc); KeInsertQueueDpc (&KiTimerExpireDpc, (PVOID) TIMER_TABLE_SIZE, NULL); // // Disable interrupts and indicate that this processor is now // in final portion of this code. // Enable = KeDisableInterrupts(); InterlockedDecrement((PLONG) &Adjust->KiNumber); // // Adjust Interrupt Time. // InterruptTime.QuadPart = KeQueryInterruptTime() + Adjust->Adjustment; SetTime.QuadPart = Adjust->Adjustment; // // Get the current times // PerfCount = KeQueryPerformanceCounter (&PerfFreq); // // Compute performance counter for current SetTime // // // Multiply SetTime * PerfCount and obtain 96bit result // in cl, li.LowPart, li.HighPart. Then divide the 96bit // result by 10,000,000 to get new performance counter value. // li.QuadPart = RtlEnlargedUnsignedMultiply ( (ULONG) SetTime.LowPart, (ULONG) PerfFreq.LowPart ).QuadPart; cl = li.LowPart; li.QuadPart = li.HighPart + RtlEnlargedUnsignedMultiply ( (ULONG) SetTime.LowPart, (ULONG) PerfFreq.HighPart ).QuadPart; li.QuadPart = li.QuadPart + RtlEnlargedUnsignedMultiply ( (ULONG) SetTime.HighPart, (ULONG) PerfFreq.LowPart ).QuadPart; li.HighPart = li.HighPart + SetTime.HighPart * PerfFreq.HighPart; divisor = 10000000; Adjust->NewCount.HighPart = RtlEnlargedUnsignedDivide ( li, divisor, &li.HighPart ); li.LowPart = cl; Adjust->NewCount.LowPart = RtlEnlargedUnsignedDivide ( li, divisor, NULL ); Adjust->NewCount.QuadPart += PerfCount.QuadPart; // // Compute tick count and tick offset for current InterruptTime // NewTickCount = RtlExtendedLargeIntegerDivide( InterruptTime, KeMaximumIncrement, &NewTickOffset ); // // Apply changes to InterruptTime, TickCount, TickOffset, and the // performance counter // KiTickOffset = KeMaximumIncrement - NewTickOffset; KeInterruptTimeBias += Adjust->Adjustment; SharedUserData->TickCountLow = NewTickCount.LowPart; #if defined(_AMD64_) || defined(_IA64_) KeTickCount = NewTickCount; #else KeTickCount.High2Time = NewTickCount.HighPart; KeTickCount.LowPart = NewTickCount.LowPart; KeTickCount.High1Time = NewTickCount.HighPart; #endif SharedUserData->InterruptTime.High2Time = InterruptTime.HighPart; SharedUserData->InterruptTime.LowPart = InterruptTime.LowPart; SharedUserData->InterruptTime.High1Time = InterruptTime.HighPart; // // Apply the performance counter change // Adjust->Barrier = 0; } HalCalibratePerformanceCounter ( (LONG volatile *) &Adjust->HalNumber, (ULONGLONG) Adjust->NewCount.QuadPart ); KeEnableInterrupts(Enable); } VOID KeSetTimeIncrement ( IN ULONG MaximumIncrement, IN ULONG MinimumIncrement ) /*++ Routine Description: This function sets the time increment value in 100ns units. This value is added to the system time at each interval clock interrupt. Arguments: MaximumIncrement - Supplies the maximum time between clock interrupts in 100ns units supported by the host HAL. MinimumIncrement - Supplies the minimum time between clock interrupts in 100ns units supported by the host HAL. Return Value: None. --*/ { KeMaximumIncrement = MaximumIncrement; KeMinimumIncrement = max(MinimumIncrement, 10 * 1000); KeTimeAdjustment = MaximumIncrement; KeTimeIncrement = MaximumIncrement; KiTickOffset = MaximumIncrement; } BOOLEAN KeAddSystemServiceTable( IN PULONG_PTR Base, IN PULONG Count OPTIONAL, IN ULONG Limit, IN PUCHAR Number, IN ULONG Index ) /*++ Routine Description: This function allows the caller to add a system service table to the system Arguments: Base - Supplies the address of the system service table dispatch table. Count - Supplies an optional pointer to a table of per system service counters. Limit - Supplies the limit of the service table. Services greater than or equal to this limit will fail. Arguments - Supplies the address of the argument count table. Index - Supplies index of the service table. Return Value: TRUE - The operation was successful. FALSE - the operation failed. A service table is already bound to the specified location, or the specified index is larger than the maximum allowed index. --*/ { PAGED_CODE(); // // If a system service table is already defined for the specified // index, then return FALSE. Otherwise, establish the new system // service table. // if ((Index > NUMBER_SERVICE_TABLES - 1) || (KeServiceDescriptorTable[Index].Base != NULL) || (KeServiceDescriptorTableShadow[Index].Base != NULL)) { return FALSE; } else { // // If the service table index is equal to the Win32 table, then // only update the shadow system service table. Otherwise, both // the shadow and static system service tables are updated. // KeServiceDescriptorTableShadow[Index].Base = Base; KeServiceDescriptorTableShadow[Index].Count = Count; KeServiceDescriptorTableShadow[Index].Limit = Limit; // // The global pointer associated with the table base is // placed just before the service table. // #if defined(_IA64_) KeServiceDescriptorTableShadow[Index].TableBaseGpOffset = (LONG)(*(Base-1) - (ULONG_PTR)Base); #endif KeServiceDescriptorTableShadow[Index].Number = Number; if (Index != 1) { KeServiceDescriptorTable[Index].Base = Base; KeServiceDescriptorTable[Index].Count = Count; KeServiceDescriptorTable[Index].Limit = Limit; #if defined(_IA64_) KeServiceDescriptorTable[Index].TableBaseGpOffset = (LONG)(*(Base-1) - (ULONG_PTR)Base); #endif KeServiceDescriptorTable[Index].Number = Number; } return TRUE; } } BOOLEAN KeRemoveSystemServiceTable( IN ULONG Index ) /*++ Routine Description: This function allows the caller to remove a system service table from the system. This can only be called at system shutdown. Arguments: Index - Supplies index of the service table. Return Value: TRUE - The operation was successful. FALSE - the operation failed. A service table is is not bound or is illegal to remove --*/ { PAGED_CODE(); if ((Index > NUMBER_SERVICE_TABLES - 1) || ((KeServiceDescriptorTable[Index].Base == NULL) && (KeServiceDescriptorTableShadow[Index].Base == NULL))) { return FALSE; } else { KeServiceDescriptorTableShadow[Index].Base = NULL; KeServiceDescriptorTableShadow[Index].Count = 0; KeServiceDescriptorTableShadow[Index].Limit = 0; #if defined(_IA64_) KeServiceDescriptorTableShadow[Index].TableBaseGpOffset = 0; #endif KeServiceDescriptorTableShadow[Index].Number = 0; if (Index != 1) { KeServiceDescriptorTable[Index].Base = NULL; KeServiceDescriptorTable[Index].Count = 0; KeServiceDescriptorTable[Index].Limit = 0; #if defined(_IA64_) KeServiceDescriptorTable[Index].TableBaseGpOffset = 0; #endif KeServiceDescriptorTable[Index].Number = 0; } return TRUE; } } VOID FASTCALL KeSetTimeUpdateNotifyRoutine( IN PTIME_UPDATE_NOTIFY_ROUTINE NotifyRoutine ) /*++ Routine Description: This function sets the address of a callout routine which will be called each time the runtime for a thread is updated. Arguments: RoutineNotify - Supplies the address of the time update notify callout routine. Return Value: None. --*/ { PAGED_CODE(); KiTimeUpdateNotifyRoutine = NotifyRoutine; return; } KAFFINITY KeQueryActiveProcessors( VOID ) /*++ Routine Description: This function returns the current set of active processors in the system. Arguments: None. Return Value: KAFFINITY bitmask representing the set of active processors --*/ { PAGED_CODE(); return(KeActiveProcessors); } #undef KeIsAttachedProcess BOOLEAN KeIsAttachedProcess( VOID ) /*++ Routine Description: This function determines if the current thread is attached to a process. Arguments: None. Return Value: TRUE is returned if the current thread is attached to a process. Otherwise, FALSE is returned. --*/ { return KiIsAttachedProcess(); } #undef KeAreApcsDisabled BOOLEAN KeAreApcsDisabled( VOID ) /*++ Routine Description: This function determines if APCs are disabled for the current thread. Arguments: None. Return Value: TRUE is returned if APCs are disabled for the current thread. Otherwise, FALE is returned. --*/ { return KeGetCurrentThread()->KernelApcDisable != 0; } ULONG KeGetRecommendedSharedDataAlignment ( VOID ) /*++ Routine Description: This function returns the size of the largest cache line in the system. This value should be used as a recommended alignment / granularity for shared data. Arguments: None. Return Value: The size of the largest cache line in the system is returned as the function value. --*/ { return KeLargestCacheLine; } PKPRCB KeGetPrcb( ULONG ProcessorNumber ) /*++ Routine Description: This function returns the address of the Processor Control Block (PRCB) for the specified processor. Arguments: ProcessorNumber - Supplies the number of the processor the PRCB is to be returned for. Return Value: Returns the address of the requested PRCB or NULL if ProcessorNumber is not valid. --*/ { ASSERT(ProcessorNumber < MAXIMUM_PROCESSORS); if (ProcessorNumber < (ULONG)KeNumberProcessors) { return KiProcessorBlock[ProcessorNumber]; } return NULL; } NTSTATUS KeCopySafe( VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, SIZE_T Length ) /*++ Routine Description: This function attempts to safely copy a block of memory. If an excpetion occurs the exception status is returned. Arguments: Destination - Supplies a pointer to the destination memory. Source - Supplies a pointer to the source memory. Length - Supplies the size of memory in bytes to be copied. Return Value: Return the status of the copy. --*/ { NTSTATUS Status = STATUS_SUCCESS; try { RtlCopyMemory(Destination, Source, Length); } __except(EXCEPTION_EXECUTE_HANDLER) { Status = _exception_code(); } return Status; }