/*++ Copyright (c) 1989 Microsoft Corporation Module Name: ki.h Abstract: This module contains the private (internal) header file for the kernel. Author: David N. Cutler (davec) 28-Feb-1989 Revision History: --*/ #ifndef _KI_ #define _KI_ #include "ntos.h" #include "stdio.h" #include "stdlib.h" #include "zwapi.h" // // Private (internal) constant definitions. // // Priority increment value definitions // #define ALERT_INCREMENT 2 // Alerted unwait priority increment #define BALANCE_INCREMENT 10 // Balance set priority increment #define RESUME_INCREMENT 0 // Resume thread priority increment #define TIMER_EXPIRE_INCREMENT 0 // Timer expiration priority increment // // Define time critical priority class base. // #define TIME_CRITICAL_PRIORITY_BOUND 14 // // Define NIL pointer value. // #define NIL (PVOID)NULL // Null pointer to void // // Define macros which are used in the kernel only // // Clear member in set // #define ClearMember(Member, Set) \ Set = Set & (~(1 << (Member))) // // Set member in set // #define SetMember(Member, Set) \ Set = Set | (1 << (Member)) // // Lock and unlock context swap lock. // #define KiLockContextSwap(OldIrql) \ *(OldIrql) = KeAcquireQueuedSpinLockRaiseToSynch(LockQueueContextSwapLock) #define KiUnlockContextSwap(OldIrql) \ KeReleaseQueuedSpinLock(LockQueueContextSwapLock, OldIrql) VOID FASTCALL KiUnlockDispatcherDatabase ( IN KIRQL OldIrql ); // VOID // KiBoostPriorityThread ( // IN PKTHREAD Thread, // IN KPRIORITY Increment // ) // //*++ // // Routine Description: // // This function boosts the priority of the specified thread using // the same algorithm used when a thread gets a boost from a wait // operation. // // Arguments: // // Thread - Supplies a pointer to a dispatcher object of type thread. // // Increment - Supplies the priority increment that is to be applied to // the thread's priority. // // Return Value: // // None. // //--* #define KiBoostPriorityThread(Thread, Increment) { \ KPRIORITY NewPriority; \ PKPROCESS Process; \ \ if ((Thread)->Priority < LOW_REALTIME_PRIORITY) { \ if ((Thread)->PriorityDecrement == 0) { \ NewPriority = (Thread)->BasePriority + (Increment); \ if (NewPriority > (Thread)->Priority) { \ if (NewPriority >= LOW_REALTIME_PRIORITY) { \ NewPriority = LOW_REALTIME_PRIORITY - 1; \ } \ \ Process = (Thread)->ApcState.Process; \ (Thread)->Quantum = Process->ThreadQuantum; \ KiSetPriorityThread((Thread), NewPriority); \ } \ } \ } \ } FORCEINLINE LOGICAL KiIsKernelStackSwappable ( IN KPROCESSOR_MODE WaitMode, IN PKTHREAD Thread ) /*++ Routine Description: This function determines whether the kernel stack is swappabel for the the specified thread in a wait operation. Arguments: WaitMode - Supplies the processor mode of the wait operation. Thread - Supplies a pointer to a dispatcher object of type thread. Return Value: If the kernel stack for the specified thread is swappable, then TRUE is returned. Otherwise, FALSE is returned. --*/ { return ((WaitMode != KernelMode) && (Thread->EnableStackSwap != FALSE) && (Thread->Priority < (LOW_REALTIME_PRIORITY + 9))); } // // Private (internal) structure definitions. // // APC Parameter structure. // typedef struct _KAPC_RECORD { PKNORMAL_ROUTINE NormalRoutine; PVOID NormalContext; PVOID SystemArgument1; PVOID SystemArgument2; } KAPC_RECORD, *PKAPC_RECORD; // // Executive initialization. // VOID ExpInitializeExecutive ( IN ULONG Number, IN PLOADER_PARAMETER_BLOCK LoaderBlock ); // // Interprocessor interrupt function definitions. // // Define immediate interprocessor commands. // #define IPI_APC 1 // APC interrupt request #define IPI_DPC 2 // DPC interrupt request #define IPI_FREEZE 4 // freeze execution request #define IPI_PACKET_READY 8 // packet ready request #define IPI_SYNCH_REQUEST 16 // reverse stall packet request // // Define interprocess interrupt types. // typedef ULONG KIPI_REQUEST; typedef ULONG_PTR (*PKIPI_BROADCAST_WORKER)( IN ULONG_PTR Argument ); #if NT_INST #define IPI_INSTRUMENT_COUNT(a,b) KiIpiCounts[a].b++; #else #define IPI_INSTRUMENT_COUNT(a,b) #endif // // Define interprocessor interrupt function prototypes. // ULONG_PTR KiIpiGenericCall ( IN PKIPI_BROADCAST_WORKER BroadcastFunction, IN ULONG_PTR Context ); #if defined(_AMD64_) || defined(_IA64_) ULONG KiIpiProcessRequests ( VOID ); #endif // defined(_AMD64_) || defined(_IA64_) VOID FASTCALL KiIpiSend ( IN KAFFINITY TargetProcessors, IN KIPI_REQUEST Request ); VOID KiIpiSendPacket ( IN KAFFINITY TargetProcessors, IN PKIPI_WORKER WorkerFunction, IN PVOID Parameter1, IN PVOID Parameter2, IN PVOID Parameter3 ); VOID FASTCALL KiIpiSignalPacketDone ( IN PKIPI_CONTEXT SignalDone ); VOID KiIpiStallOnPacketTargets ( KAFFINITY TargetSet ); // // Private (internal) function definitions. // VOID FASTCALL KiActivateWaiterQueue ( IN PKQUEUE Queue ); BOOLEAN KiAdjustInterruptTime ( IN LONGLONG TimeDelta ); VOID KiAllProcessorsStarted ( VOID ); VOID KiApcInterrupt ( VOID ); NTSTATUS KiCallUserMode ( IN PVOID *OutputBuffer, IN PULONG OutputLength ); typedef struct { ULONGLONG Adjustment; LARGE_INTEGER NewCount; volatile LONG KiNumber; volatile LONG HalNumber; volatile LONG Barrier; } ADJUST_INTERRUPT_TIME_CONTEXT, *PADJUST_INTERRUPT_TIME_CONTEXT; VOID KiCalibrateTimeAdjustment ( PADJUST_INTERRUPT_TIME_CONTEXT Adjust ); VOID KiChainedDispatch ( VOID ); #if DBG VOID KiCheckTimerTable ( IN ULARGE_INTEGER SystemTime ); #endif LARGE_INTEGER KiComputeReciprocal ( IN LONG Divisor, OUT PCCHAR Shift ); ULONG KiComputeTimerTableIndex ( IN LARGE_INTEGER Interval, IN LARGE_INTEGER CurrentCount, IN PKTIMER Timer ); PLARGE_INTEGER FASTCALL KiComputeWaitInterval ( IN PLARGE_INTEGER OriginalTime, IN PLARGE_INTEGER DueTime, IN OUT PLARGE_INTEGER NewTime ); NTSTATUS KiContinue ( IN PCONTEXT ContextRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame ); VOID KiDeliverApc ( IN KPROCESSOR_MODE PreviousMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame ); VOID KiDispatchException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance ); KCONTINUE_STATUS KiSetDebugProcessor ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN KPROCESSOR_MODE PreviousMode ); ULONG KiCopyInformation ( IN OUT PEXCEPTION_RECORD ExceptionRecord1, IN PEXCEPTION_RECORD ExceptionRecord2 ); VOID KiDispatchInterrupt ( VOID ); PKTHREAD FASTCALL KiFindReadyThread ( IN ULONG Processor, KPRIORITY LowPriority ); VOID KiFloatingDispatch ( VOID ); #if !defined(_IA64_) && !defined(_AMD64_) VOID FASTCALL KiFlushSingleTb ( IN BOOLEAN Invalid, IN PVOID Virtual ); #endif // !_IA64_ && !_AMD64_ VOID KiFlushMultipleTb ( IN BOOLEAN Invalid, IN PVOID *Virtual, IN ULONG Count ); // // VOID // KiSetTbFlushTimeStampBusy ( // VOID // ) // //*++ // // Routine Description: // // This function sets the TB flush time stamp busy by setting the high // order bit of the TB flush time stamp. All readers of the time stamp // value will spin until the bit is cleared. // // Arguments: // // None. // // Return Value: // // None. // //--* __inline VOID KiSetTbFlushTimeStampBusy ( VOID ) { LONG Value; // // While the TB flush time stamp counter is being updated the high // order bit of the time stamp value is set. Otherwise, the bit is // clear. // do { do { } while ((Value = KiTbFlushTimeStamp) < 0); // // Attempt to set the high order bit. // } while (InterlockedCompareExchange((PLONG)&KiTbFlushTimeStamp, Value | 0x80000000, Value) != Value); return; } // VOID // KiClearTbFlushTimeStampBusy ( // VOID // ) // //*++ // // Routine Description: // // This function ckears the TB flush time stamp busy by clearing the high // order bit of the TB flush time stamp and incrementing the low 32-bit // value. // // N.B. It is assumed that the high order bit of the time stamp value // is set on entry to this routine. // // Arguments: // // None. // // Return Value: // // None. // //--* __inline VOID KiClearTbFlushTimeStampBusy ( VOID ) { LONG Value; // // Get the current TB flush time stamp value, compute the next value, // and store the result clearing the busy bit. // Value = (KiTbFlushTimeStamp + 1) & 0x7fffffff; InterlockedExchange((PLONG)&KiTbFlushTimeStamp, Value); return; } PULONG KiGetUserModeStackAddress ( VOID ); VOID KiInitializeContextThread ( IN PKTHREAD Thread, IN PKSYSTEM_ROUTINE SystemRoutine, IN PKSTART_ROUTINE StartRoutine OPTIONAL, IN PVOID StartContext OPTIONAL, IN PCONTEXT ContextFrame OPTIONAL ); VOID KiInitializeKernel ( IN PKPROCESS Process, IN PKTHREAD Thread, IN PVOID IdleStack, IN PKPRCB Prcb, IN CCHAR Number, IN PLOADER_PARAMETER_BLOCK LoaderBlock ); VOID KiInitQueuedSpinLocks ( PKPRCB Prcb, ULONG Number ); VOID KiInitSystem ( VOID ); BOOLEAN KiInitMachineDependent ( VOID ); VOID KiInitializeUserApc ( IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN PKNORMAL_ROUTINE NormalRoutine, IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); LONG FASTCALL KiInsertQueue ( IN PKQUEUE Queue, IN PLIST_ENTRY Entry, IN BOOLEAN Head ); BOOLEAN FASTCALL KiInsertQueueApc ( IN PKAPC Apc, IN KPRIORITY Increment ); LOGICAL FASTCALL KiInsertTreeTimer ( IN PKTIMER Timer, IN LARGE_INTEGER Interval ); VOID KiInterruptDispatch ( VOID ); VOID KiInterruptDispatchRaise ( IN PKINTERRUPT Interrupt ); VOID KiInterruptDispatchSame ( IN PKINTERRUPT Interrupt ); VOID KiPassiveRelease ( VOID ); PKTHREAD KiQuantumEnd ( VOID ); NTSTATUS KiRaiseException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN BOOLEAN FirstChance ); VOID FASTCALL KiReadyThread ( IN PKTHREAD Thread ); LOGICAL FASTCALL KiReinsertTreeTimer ( IN PKTIMER Timer, IN ULARGE_INTEGER DueTime ); #if DBG #define KiRemoveTreeTimer(Timer) \ (Timer)->Header.Inserted = FALSE; \ RemoveEntryList(&(Timer)->TimerListEntry); \ (Timer)->TimerListEntry.Flink = NULL; \ (Timer)->TimerListEntry.Blink = NULL #else #define KiRemoveTreeTimer(Timer) \ (Timer)->Header.Inserted = FALSE; \ RemoveEntryList(&(Timer)->TimerListEntry) #endif #if defined(NT_UP) #define KiRequestApcInterrupt(Processor) KiRequestSoftwareInterrupt(APC_LEVEL) #else #define KiRequestApcInterrupt(Processor) \ if (KeGetCurrentPrcb()->Number == (CCHAR)Processor) { \ KiRequestSoftwareInterrupt(APC_LEVEL); \ } else { \ KiIpiSend(AFFINITY_MASK(Processor), IPI_APC); \ } #endif #if defined(NT_UP) #define KiRequestDispatchInterrupt(Processor) #else #define KiRequestDispatchInterrupt(Processor) \ if (KeGetCurrentPrcb()->Number != (CCHAR)Processor) { \ KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC); \ } #endif PKTHREAD FASTCALL KiSelectNextThread ( IN ULONG Processor ); KAFFINITY FASTCALL KiSetAffinityThread ( IN PKTHREAD Thread, IN KAFFINITY Affinity ); VOID KiSetSystemTime ( IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime ); VOID KiSuspendNop ( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine, IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 ); VOID KiSuspendRundown ( IN PKAPC Apc ); VOID KiSuspendThread ( IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); BOOLEAN KiSwapProcess ( IN PKPROCESS NewProcess, IN PKPROCESS OldProcess ); LONG_PTR FASTCALL KiSwapThread ( VOID ); VOID KiThreadStartup ( IN PVOID StartContext ); VOID KiTimerExpiration ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID FASTCALL KiTimerListExpire ( IN PLIST_ENTRY ExpiredListHead, IN KIRQL OldIrql ); VOID KiUnexpectedInterrupt ( VOID ); VOID FASTCALL KiUnlinkThread ( IN PKTHREAD Thread, IN LONG_PTR WaitStatus ); VOID FASTCALL KiUnwaitThread ( IN PKTHREAD Thread, IN LONG_PTR WaitStatus, IN KPRIORITY Increment, IN PLIST_ENTRY ThreadList OPTIONAL ); VOID KiUserApcDispatcher ( IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2, IN PKNORMAL_ROUTINE NormalRoutine ); VOID KiUserExceptionDispatcher ( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextFrame ); BOOLEAN FASTCALL KiSwapContext ( IN PKTHREAD Thread ); VOID FASTCALL KiWaitSatisfyAll ( IN PKWAIT_BLOCK WaitBlock ); // // VOID // FASTCALL // KiWaitSatisfyAny ( // IN PKMUTANT Object, // IN PKTHREAD Thread // ) // // // Routine Description: // // This function satisfies a wait for any type of object and performs // any side effects that are necessary. // // Arguments: // // Object - Supplies a pointer to a dispatcher object. // // Thread - Supplies a pointer to a dispatcher object of type thread. // // Return Value: // // None. // #define KiWaitSatisfyAny(_Object_, _Thread_) { \ if (((_Object_)->Header.Type & DISPATCHER_OBJECT_TYPE_MASK) == EventSynchronizationObject) { \ (_Object_)->Header.SignalState = 0; \ \ } else if ((_Object_)->Header.Type == SemaphoreObject) { \ (_Object_)->Header.SignalState -= 1; \ \ } else if ((_Object_)->Header.Type == MutantObject) { \ (_Object_)->Header.SignalState -= 1; \ if ((_Object_)->Header.SignalState == 0) { \ (_Thread_)->KernelApcDisable -= (_Object_)->ApcDisable; \ (_Object_)->OwnerThread = (_Thread_); \ if ((_Object_)->Abandoned == TRUE) { \ (_Object_)->Abandoned = FALSE; \ (_Thread_)->WaitStatus = STATUS_ABANDONED; \ } \ \ InsertHeadList((_Thread_)->MutantListHead.Blink, \ &(_Object_)->MutantListEntry); \ } \ } \ } // // VOID // FASTCALL // KiWaitSatisfyMutant ( // IN PKMUTANT Object, // IN PKTHREAD Thread // ) // // // Routine Description: // // This function satisfies a wait for a mutant object. // // Arguments: // // Object - Supplies a pointer to a dispatcher object. // // Thread - Supplies a pointer to a dispatcher object of type thread. // // Return Value: // // None. // #define KiWaitSatisfyMutant(_Object_, _Thread_) { \ (_Object_)->Header.SignalState -= 1; \ if ((_Object_)->Header.SignalState == 0) { \ (_Thread_)->KernelApcDisable -= (_Object_)->ApcDisable; \ (_Object_)->OwnerThread = (_Thread_); \ if ((_Object_)->Abandoned == TRUE) { \ (_Object_)->Abandoned = FALSE; \ (_Thread_)->WaitStatus = STATUS_ABANDONED; \ } \ \ InsertHeadList((_Thread_)->MutantListHead.Blink, \ &(_Object_)->MutantListEntry); \ } \ } // // VOID // FASTCALL // KiWaitSatisfyOther ( // IN PKMUTANT Object // ) // // // Routine Description: // // This function satisfies a wait for any type of object except a mutant // and performs any side effects that are necessary. // // Arguments: // // Object - Supplies a pointer to a dispatcher object. // // Return Value: // // None. // #define KiWaitSatisfyOther(_Object_) { \ if (((_Object_)->Header.Type & DISPATCHER_OBJECT_TYPE_MASK) == EventSynchronizationObject) { \ (_Object_)->Header.SignalState = 0; \ \ } else if ((_Object_)->Header.Type == SemaphoreObject) { \ (_Object_)->Header.SignalState -= 1; \ \ } \ } VOID FASTCALL KiWaitTest ( IN PVOID Object, IN KPRIORITY Increment ); VOID KiFreezeTargetExecution ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame ); VOID KiPollFreezeExecution ( VOID ); VOID KiSaveProcessorState ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame ); VOID KiSaveProcessorControlState ( IN PKPROCESSOR_STATE ProcessorState ); VOID KiRestoreProcessorState ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame ); VOID KiRestoreProcessorControlState ( IN PKPROCESSOR_STATE ProcessorState ); #define KiEnableAlignmentExceptions() #define KiDisableAlignmentExceptions() BOOLEAN KiHandleAlignmentFault( IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance, OUT BOOLEAN *ExceptionForwarded ); // // External references to private kernel data structures // extern PMESSAGE_RESOURCE_DATA KiBugCodeMessages; extern ULONG KiDmaIoCoherency; extern ULONG KiMaximumDpcQueueDepth; extern ULONG KiMinimumDpcRate; extern ULONG KiAdjustDpcThreshold; extern PKDEBUG_ROUTINE KiDebugRoutine; extern PKDEBUG_SWITCH_ROUTINE KiDebugSwitchRoutine; extern LIST_ENTRY KiDispatcherReadyListHead[MAXIMUM_PRIORITY]; extern const CCHAR KiFindFirstSetLeft[256]; extern CALL_PERFORMANCE_DATA KiFlushSingleCallData; extern ULONG_PTR KiHardwareTrigger; extern KAFFINITY KiIdleSummary; extern KAFFINITY KiIdleSMTSummary; extern KEVENT KiSwapEvent; extern PKTHREAD KiSwappingThread; extern KNODE KiNode0; extern KNODE KiNodeInit[]; extern SINGLE_LIST_ENTRY KiProcessInSwapListHead; extern SINGLE_LIST_ENTRY KiProcessOutSwapListHead; extern SINGLE_LIST_ENTRY KiStackInSwapListHead; extern LIST_ENTRY KiProfileSourceListHead; extern BOOLEAN KiProfileAlignmentFixup; extern ULONG KiProfileAlignmentFixupInterval; extern ULONG KiProfileAlignmentFixupCount; #if defined(_IA64_) // KiProfileInterval value should be replaced by a call: // HalQuerySystemInformation(HalProfileSourceInformation) #else // !_IA64_ extern ULONG KiProfileInterval; #endif // !_IA64_ extern LIST_ENTRY KiProfileListHead; extern KSPIN_LOCK KiProfileLock; extern ULONG KiReadySummary; extern UCHAR KiArgumentTable[]; extern ULONG KiServiceLimit; extern ULONG_PTR KiServiceTable[]; extern CALL_PERFORMANCE_DATA KiSetEventCallData; extern ULONG KiTickOffset; extern LARGE_INTEGER KiTimeIncrementReciprocal; extern CCHAR KiTimeIncrementShiftCount; extern LIST_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; extern KAFFINITY KiTimeProcessor; extern KDPC KiTimerExpireDpc; extern KSPIN_LOCK KiFreezeExecutionLock; extern BOOLEAN KiSlavesStartExecution; extern PTIME_UPDATE_NOTIFY_ROUTINE KiTimeUpdateNotifyRoutine; extern LIST_ENTRY KiWaitListHead; extern CALL_PERFORMANCE_DATA KiWaitSingleCallData; extern ULONG KiEnableTimerWatchdog; #if defined(_IA64_) extern ULONG KiMasterRid; extern ULONGLONG KiMasterSequence; extern ULONG KiIdealDpcRate; #if !defined(UP_NT) extern KSPIN_LOCK KiMasterRidLock; #endif VOID KiSaveEmDebugContext ( IN OUT PCONTEXT Context ); VOID KiLoadEmDebugContext ( IN PCONTEXT Context ); VOID KiFlushRse ( VOID ); VOID KiInvalidateStackedRegisters ( VOID ); NTSTATUS Ki386CheckDivideByZeroTrap( IN PKTRAP_FRAME Frame ); #endif // defined(_IA64_) #if defined(_IA64_) extern KINTERRUPT KxUnexpectedInterrupt; #endif #if NT_INST extern KIPI_COUNTS KiIpiCounts[MAXIMUM_PROCESSORS]; #endif extern KSPIN_LOCK KiFreezeLockBackup; extern ULONG KiFreezeFlag; extern volatile ULONG KiSuspendState; #if DBG extern ULONG KiMaximumSearchCount; #endif // VOID // KiSetSwapEvent ( // VOID // ) // //*++ // // Routine Description: // // This function sets the swap event or unwaits the swap thread. // // N.B. The dispatcher lock must be held to call this routine. // // Arguments: // // None. // // Return Value: // // None. // //--* __inline VOID KiSetSwapEvent ( VOID ) { PLIST_ENTRY WaitEntry; // // If the swap event wait queue is not empty, then unwait the swap // thread (there is only one swap thread). Otherwise, set the swap // event. // WaitEntry = KiSwapEvent.Header.WaitListHead.Flink; if (WaitEntry != &KiSwapEvent.Header.WaitListHead) { KiUnwaitThread(KiSwappingThread, 0, BALANCE_INCREMENT, NULL); } else { KiSwapEvent.Header.SignalState = 1; } return; } // // Include platform specific internal kernel header file. // #if defined(_AMD64_) #include "amd64\kiamd64.h" #elif defined(_X86_) #include "i386\kix86.h" #endif // defined(_AMD64_) #endif // defined(_KI_)