summaryrefslogtreecommitdiffstats
path: root/private/ntos/ke/procobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ke/procobj.c')
-rw-r--r--private/ntos/ke/procobj.c858
1 files changed, 858 insertions, 0 deletions
diff --git a/private/ntos/ke/procobj.c b/private/ntos/ke/procobj.c
new file mode 100644
index 000000000..850918b1b
--- /dev/null
+++ b/private/ntos/ke/procobj.c
@@ -0,0 +1,858 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ procobj.c
+
+Abstract:
+
+ This module implements the machine independent functions to manipulate
+ the kernel process object. Functions are provided to initilaize, attach,
+ detach, exclude, include, and set the base priority of process objects.
+
+Author:
+
+ David N. Cutler (davec) 7-Mar-1989
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "ki.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, KeInitializeProcess)
+#endif
+
+
+//
+// Define forward referenced function prototypes.
+//
+
+VOID
+KiAttachProcess (
+ IN PKPROCESS Process,
+ IN KIRQL OldIrql
+ );
+
+VOID
+KiMoveApcState (
+ IN PKAPC_STATE Source,
+ OUT PKAPC_STATE Destination
+ );
+
+//
+// The following assert macro is used to check that an input process is
+// really a kprocess and not something else, like deallocated pool.
+//
+
+#define ASSERT_PROCESS(E) { \
+ ASSERT((E)->Header.Type == ProcessObject); \
+}
+
+
+VOID
+KeInitializeProcess (
+ IN PRKPROCESS Process,
+ IN KPRIORITY BasePriority,
+ IN KAFFINITY Affinity,
+ IN ULONG DirectoryTableBase[2],
+ IN BOOLEAN Enable
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes a kernel process object. The base priority,
+ affinity, and page frame numbers for the process page table directory
+ and hyper space are stored in the process object.
+
+ N.B. It is assumed that the process object is zeroed.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+ BasePriority - Supplies the base priority of the process.
+
+ Affinity - Supplies the set of processors on which children threads
+ of the process can execute.
+
+ DirectoryTableBase - Supplies a pointer to an array whose fist element
+ is the value that is to be loaded into the Directory Table Base
+ register when a child thread is dispatched for execution and whose
+ second element contains the page table entry that maps hyper space.
+
+ Enable - Supplies a boolean value that determines the default
+ handling of data alignment exceptions for child threads. A value
+ of TRUE causes all data alignment exceptions to be automatically
+ handled by the kernel. A value of FALSE causes all data alignment
+ exceptions to be actually raised as exceptions.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Initialize the standard dispatcher object header and set the initial
+ // signal state of the process object.
+ //
+
+ Process->Header.Type = ProcessObject;
+ Process->Header.Size = sizeof(KPROCESS) / sizeof(LONG);
+ InitializeListHead(&Process->Header.WaitListHead);
+
+ //
+ // Initialize the base priority, affinity, directory table base values,
+ // autoalignment, and stack count.
+ //
+ // N.B. The distinguished value MAXSHORT is used to signify that no
+ // threads have been created for the process.
+ //
+
+ Process->BasePriority = (SCHAR)BasePriority;
+ Process->Affinity = Affinity;
+ Process->AutoAlignment = Enable;
+ Process->DirectoryTableBase[0] = DirectoryTableBase[0];
+ Process->DirectoryTableBase[1] = DirectoryTableBase[1];
+ Process->StackCount = MAXSHORT;
+
+ //
+ // Initialize the stack count, profile listhead, ready queue list head,
+ // accumulated runtime, process quantum, thread quantum, and thread list
+ // head.
+ //
+
+ InitializeListHead(&Process->ProfileListHead);
+ InitializeListHead(&Process->ReadyListHead);
+ InitializeListHead(&Process->ThreadListHead);
+ Process->ThreadQuantum = THREAD_QUANTUM;
+
+ //
+ // Initialize the process state and set the thread processor selection
+ // seed.
+ //
+
+ Process->State = ProcessInMemory;
+ Process->ThreadSeed = (UCHAR)KiQueryLowTickCount();
+
+ //
+ // Initialize Ldt descriptor for this process (i386 only)
+ //
+
+#ifdef i386
+
+ //
+ // Initialize IopmBase and Iopl flag for this process (i386 only)
+ //
+
+ Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
+
+#endif
+
+ return;
+}
+
+VOID
+KeAttachProcess (
+ IN PRKPROCESS Process
+ )
+
+/*++
+
+Routine Description:
+
+ This function attaches a thread to a target process' address space.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+
+ ASSERT_PROCESS(Process);
+ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Attach target process.
+ //
+
+ KiAttachProcess(Process, OldIrql);
+ return;
+}
+
+BOOLEAN
+KeTryToAttachProcess (
+ IN PRKPROCESS Process
+ )
+
+/*++
+
+Routine Description:
+
+ This function tries to attach a thread to a target process' address
+ space. If the target process is in memory or out of memory, then the
+ target process is attached. Otherwise, it is not attached.
+
+ N.B. If the target process state is out of memory, then the caller
+ must have all pages for the process in memory. This function is
+ intended for use by the memory management system.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+Return Value:
+
+ If the target process state is not in transistion, then the target
+ process is atached and a value of TRUE is returned. Otherwise, a
+ value of FALSE is returned.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+
+ ASSERT_PROCESS(Process);
+ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // If the target process state is not in transition, then set the
+ // target process state to in memory, attach the process, and return
+ // a value of TRUE. Otherwise, unlock the dispatcher database and
+ // return a value of FALSE.
+ //
+
+ if (Process->State != ProcessInTransition) {
+ Process->State = ProcessInMemory;
+ KiAttachProcess(Process, OldIrql);
+ return TRUE;
+
+ } else {
+ KiUnlockDispatcherDatabase(OldIrql);
+ return FALSE;
+ }
+}
+
+VOID
+KeDetachProcess (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function detaches a thread from another process' address space.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ PKPROCESS Process;
+ PKTHREAD Thread;
+
+ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ Thread = KeGetCurrentThread();
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // If the current thread is attached to another address, then detach
+ // it.
+ //
+
+ if (Thread->ApcStateIndex != 0) {
+
+ //
+ // Check if a kernel APC is in progress, the kernel APC queue is
+ // not empty, or the user APC queue is not empty. If any of these
+ // conditions are true, then call bug check.
+ //
+
+#if DBG
+
+ if ((Thread->ApcState.KernelApcInProgress) ||
+ (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) ||
+ (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE)) {
+ KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
+ }
+
+#endif
+
+ //
+ // Unbias current process stack count and check if the process should
+ // be swapped out of memory.
+ //
+
+ Process = Thread->ApcState.Process;
+ Process->StackCount -= 1;
+ if (Process->StackCount == 0) {
+ Process->State = ProcessInTransition;
+ InsertTailList(&KiProcessOutSwapListHead, &Process->SwapListEntry);
+ KiSwapEvent.Header.SignalState = 1;
+ if (IsListEmpty(&KiSwapEvent.Header.WaitListHead) == FALSE) {
+ KiWaitTest(&KiSwapEvent, BALANCE_INCREMENT);
+ }
+ }
+
+ //
+ // Restore APC state and check whether the kernel APC queue contains
+ // an entry. If the kernel APC queue contains an entry then set kernel
+ // APC pending and request a software interrupt at APC_LEVEL.
+ //
+
+ KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
+ Thread->SavedApcState.Process = (PKPROCESS)NULL;
+ Thread->ApcStatePointer[0] = &Thread->ApcState;
+ Thread->ApcStatePointer[1] = &Thread->SavedApcState;
+ Thread->ApcStateIndex = 0;
+ if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
+ Thread->ApcState.KernelApcPending = TRUE;
+ KiRequestSoftwareInterrupt(APC_LEVEL);
+ }
+
+ //
+ // Swap the address space back to the parent process.
+ //
+
+ KiSwapProcess(Thread->ApcState.Process, Process);
+
+ }
+
+ //
+ // Lower IRQL to its previous value and return.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+ return;
+}
+
+LONG
+KeReadStateProcess (
+ IN PRKPROCESS Process
+ )
+
+/*++
+
+Routine Description:
+
+ This function reads the current signal state of a process object.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+Return Value:
+
+ The current signal state of the process object.
+
+--*/
+
+{
+
+ ASSERT_PROCESS(Process);
+
+ //
+ // Return current signal state of process object.
+ //
+
+ return Process->Header.SignalState;
+}
+
+LONG
+KeSetProcess (
+ IN PRKPROCESS Process,
+ IN KPRIORITY Increment,
+ IN BOOLEAN Wait
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets the signal state of a proces object to Signaled
+ and attempts to satisfy as many Waits as possible. The previous
+ signal state of the process object is returned as the function value.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+ Increment - Supplies the priority increment that is to be applied
+ if setting the process causes a Wait to be satisfied.
+
+ Wait - Supplies a boolean value that signifies whether the call to
+ KeSetProcess will be immediately followed by a call to one of the
+ kernel Wait functions.
+
+Return Value:
+
+ The previous signal state of the process object.
+
+--*/
+
+{
+
+ KIRQL OldIrql;
+ LONG OldState;
+ PRKTHREAD Thread;
+
+ ASSERT_PROCESS(Process);
+ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // If the previous state of the process object is Not-Signaled and
+ // the wait queue is not empty, then satisfy as many Waits as
+ // possible.
+ //
+
+ OldState = Process->Header.SignalState;
+ Process->Header.SignalState = 1;
+ if ((OldState == 0) && (!IsListEmpty(&Process->Header.WaitListHead))) {
+ KiWaitTest(Process, Increment);
+ }
+
+ //
+ // If the value of the Wait argument is TRUE, then return to the
+ // caller with IRQL raised and the dispatcher database locked. Else
+ // release the dispatcher database lock and lower IRQL to its
+ // previous value.
+ //
+
+ if (Wait) {
+ Thread = KeGetCurrentThread();
+ Thread->WaitNext = Wait;
+ Thread->WaitIrql = OldIrql;
+
+ } else {
+ KiUnlockDispatcherDatabase(OldIrql);
+ }
+
+ //
+ // Return previous signal state of process object.
+ //
+
+ return OldState;
+}
+
+KPRIORITY
+KeSetPriorityProcess (
+ IN PKPROCESS Process,
+ IN KPRIORITY NewBase
+ )
+
+/*++
+
+Routine Description:
+
+ This function set the base priority of a process to a new value
+ and adjusts the priority and base priority of all child threads
+ as appropriate.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+ NewBase - Supplies the new base priority of the process.
+
+Return Value:
+
+ The previous base priority of the process.
+
+--*/
+
+{
+
+ KPRIORITY Adjustment;
+ PLIST_ENTRY NextEntry;
+ KPRIORITY NewPriority;
+ KIRQL OldIrql;
+ KPRIORITY OldBase;
+ PKTHREAD Thread;
+
+ ASSERT_PROCESS(Process);
+ ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ //
+ // Raise IRQL to dispatcher level and lock dispatcher database.
+ //
+
+ KiLockDispatcherDatabase(&OldIrql);
+
+ //
+ // Save the current process base priority, set the new process base
+ // priority, compute the adjustment value, and adjust the priority
+ // and base priority of all child threads as appropriate.
+ //
+
+ OldBase = Process->BasePriority;
+ Process->BasePriority = (SCHAR)NewBase;
+ Adjustment = NewBase - OldBase;
+ NextEntry = Process->ThreadListHead.Flink;
+ if (NewBase >= LOW_REALTIME_PRIORITY) {
+ while (NextEntry != &Process->ThreadListHead) {
+ Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
+
+ //
+ // Compute the new base priority of the thread.
+ //
+
+ NewPriority = Thread->BasePriority + Adjustment;
+
+ //
+ // If the new base priority is outside the realtime class,
+ // then limit the change to the realtime class.
+ //
+
+ if (NewPriority < LOW_REALTIME_PRIORITY) {
+ NewPriority = LOW_REALTIME_PRIORITY;
+
+ } else if (NewPriority > HIGH_PRIORITY) {
+ NewPriority = HIGH_PRIORITY;
+ }
+
+ //
+ // Set the base priority and the current priority of the
+ // thread to the computed value and reset the thread quantum.
+ //
+ // N.B. If priority saturation occured the last time the thread
+ // base priority was set and the new process base priority
+ // is not crossing from variable to realtime, then the thread
+ // priority is not changed.
+ //
+
+ if ((Thread->Saturation == FALSE) || (OldBase < LOW_REALTIME_PRIORITY)) {
+ Thread->BasePriority = (SCHAR)NewPriority;
+ Thread->Quantum = Process->ThreadQuantum;
+ Thread->DecrementCount = 0;
+ Thread->PriorityDecrement = 0;
+ Thread->Saturation = FALSE;
+ KiSetPriorityThread(Thread, NewPriority);
+ }
+
+ NextEntry = NextEntry->Flink;
+ }
+
+ } else {
+ while (NextEntry != &Process->ThreadListHead) {
+ Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
+
+ //
+ // Compute the new base priority of the thread.
+ //
+
+ NewPriority = Thread->BasePriority + Adjustment;
+
+ //
+ // If the new base priority is outside the variable class,
+ // then limit the change to the variable class.
+ //
+
+ if (NewPriority >= LOW_REALTIME_PRIORITY) {
+ NewPriority = LOW_REALTIME_PRIORITY - 1;
+
+ } else if (NewPriority <= LOW_PRIORITY) {
+ NewPriority = 1;
+ }
+
+ //
+ // Set the base priority and the current priority of the
+ // thread to the computed value and reset the thread quantum.
+ //
+ // N.B. If priority saturation occured the last time the thread
+ // base priority was set and the new process base priority
+ // is not crossing from realtime to variable, then the thread
+ // priority is not changed.
+ //
+
+ if ((Thread->Saturation == FALSE) || (OldBase >= LOW_REALTIME_PRIORITY)) {
+ Thread->BasePriority = (SCHAR)NewPriority;
+ Thread->Quantum = Process->ThreadQuantum;
+ Thread->DecrementCount = 0;
+ Thread->PriorityDecrement = 0;
+ Thread->Saturation = FALSE;
+ KiSetPriorityThread(Thread, NewPriority);
+ }
+
+ NextEntry = NextEntry->Flink;
+ }
+ }
+
+ //
+ // Unlock dispatcher database and lower IRQL to its previous
+ // value.
+ //
+
+ KiUnlockDispatcherDatabase(OldIrql);
+
+ //
+ // Return previous process base priority
+ //
+
+ return OldBase;
+}
+
+VOID
+KiAttachProcess (
+ IN PKPROCESS Process,
+ IN KIRQL OldIrql
+ )
+
+/*++
+
+Routine Description:
+
+ This function attaches a thread to a target process' address space.
+
+ N.B. The dispatcher database lock must be held when this routine is
+ called.
+
+Arguments:
+
+ Process - Supplies a pointer to a dispatcher object of type process.
+
+ Thread - Supplies a pointer to a dispatcher object of type thread.
+
+ OldIrql - Supplies the previous IRQL.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PRKTHREAD Thread;
+ KAFFINITY Processor;
+
+ //
+ // Get the address of the current thread object.
+ //
+
+ Thread = KeGetCurrentThread();
+
+ //
+ // Check whether there is already a process address space attached or
+ // the thread is executing a DPC. If either condition is true, then call
+ // bug check.
+ //
+
+ if (Process != Thread->ApcState.Process) {
+ if ((Thread->ApcStateIndex != 0) ||
+ (KeIsExecutingDpc() != FALSE)) {
+ KeBugCheckEx(
+ INVALID_PROCESS_ATTACH_ATTEMPT,
+ (ULONG)Process,
+ (ULONG)Thread->ApcState.Process,
+ (ULONG)Thread->ApcStateIndex,
+ (ULONG)KeIsExecutingDpc()
+ );
+ }
+ }
+
+ //
+ // If the target process is the same as the current process, then
+ // there is no need to attach the address space. Otherwise, attach
+ // the current thread to the target thread address space.
+ //
+
+ if (Process == Thread->ApcState.Process) {
+ KiUnlockDispatcherDatabase(OldIrql);
+
+ } else {
+
+ //
+ // Bias the stack count of the target process to signify that a
+ // thread exists in that process with a stack that is resident.
+ //
+
+ Process->StackCount += 1;
+
+ //
+ // Save current APC state and initialize A new APC state.
+ //
+
+ KiMoveApcState(&Thread->ApcState, &Thread->SavedApcState);
+ InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
+ InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
+ Thread->ApcState.Process = Process;
+ Thread->ApcState.KernelApcInProgress = FALSE;
+ Thread->ApcState.KernelApcPending = FALSE;
+ Thread->ApcState.UserApcPending = FALSE;
+ Thread->ApcStatePointer[0] = &Thread->SavedApcState;
+ Thread->ApcStatePointer[1] = &Thread->ApcState;
+ Thread->ApcStateIndex = 1;
+
+ //
+ // If the target process is in memory, then immediately enter the
+ // new address space by loading a new Directory Table Base. Otherwise,
+ // insert the current thread in the target process ready list, inswap
+ // the target process if necessary, select a new thread to run on the
+ // the current processor and context switch to the new thread.
+ //
+
+ if (Process->State == ProcessInMemory) {
+ KiSwapProcess(Process, Thread->SavedApcState.Process);
+ KiUnlockDispatcherDatabase(OldIrql);
+
+ } else {
+ Thread->State = Ready;
+ Thread->ProcessReadyQueue = TRUE;
+ InsertTailList(&Process->ReadyListHead, &Thread->WaitListEntry);
+ if (Process->State == ProcessOutOfMemory) {
+ Process->State = ProcessInTransition;
+ InsertTailList(&KiProcessInSwapListHead, &Process->SwapListEntry);
+ KiSwapEvent.Header.SignalState = 1;
+ if (IsListEmpty(&KiSwapEvent.Header.WaitListHead) == FALSE) {
+ KiWaitTest(&KiSwapEvent, BALANCE_INCREMENT);
+ }
+ }
+
+ //
+ // Clear the active processor bit in the previous process and
+ // set active processor bit in the process being attached to.
+ //
+
+#if !defined(NT_UP)
+
+ Processor = KeGetCurrentPrcb()->SetMember;
+ Thread->SavedApcState.Process->ActiveProcessors &= ~Processor;
+ Process->ActiveProcessors |= Processor;
+
+#endif
+
+ Thread->WaitIrql = OldIrql;
+
+ KiSwapThread();
+
+ }
+ }
+
+ return;
+}
+
+VOID
+KiMoveApcState (
+ IN PKAPC_STATE Source,
+ OUT PKAPC_STATE Destination
+ )
+
+/*++
+
+Routine Description:
+
+ This function moves the APC state from the source structure to the
+ destination structure and reinitializes list headers as appropriate.
+
+Arguments:
+
+ Source - Supplies a pointer to the source APC state structure.
+
+ Destination - Supplies a pointer to the destination APC state structure.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PLIST_ENTRY First;
+ PLIST_ENTRY Last;
+
+ //
+ // Copy the APC state from the source to the destination.
+ //
+
+#if defined(_M_PPC) && (_MSC_VER >= 1000)
+ KIRQL OldIrql;
+ KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+#endif
+
+ *Destination = *Source;
+
+#if defined(_M_PPC) && (_MSC_VER >= 1000)
+ KeLowerIrql(OldIrql);
+#endif
+
+ if (IsListEmpty(&Source->ApcListHead[KernelMode]) != FALSE) {
+ InitializeListHead(&Destination->ApcListHead[KernelMode]);
+
+ } else {
+ First = Source->ApcListHead[KernelMode].Flink;
+ Last = Source->ApcListHead[KernelMode].Blink;
+ Destination->ApcListHead[KernelMode].Flink = First;
+ Destination->ApcListHead[KernelMode].Blink = Last;
+ First->Blink = &Destination->ApcListHead[KernelMode];
+ Last->Flink = &Destination->ApcListHead[KernelMode];
+ }
+
+ if (IsListEmpty(&Source->ApcListHead[UserMode]) != FALSE) {
+ InitializeListHead(&Destination->ApcListHead[UserMode]);
+
+ } else {
+ First = Source->ApcListHead[UserMode].Flink;
+ Last = Source->ApcListHead[UserMode].Blink;
+ Destination->ApcListHead[UserMode].Flink = First;
+ Destination->ApcListHead[UserMode].Blink = Last;
+ First->Blink = &Destination->ApcListHead[UserMode];
+ Last->Flink = &Destination->ApcListHead[UserMode];
+ }
+
+ return;
+}