2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/process.c
5 * PURPOSE: Kernel Process Management and System Call Tables
6 * PROGRAMMERS: Alex Ionescu
10 /* INCLUDES *****************************************************************/
14 #include <internal/debug.h>
16 /* GLOBALS *****************************************************************/
18 LIST_ENTRY KiProcessListHead
;
19 LIST_ENTRY KiProcessInSwapListHead
, KiProcessOutSwapListHead
;
20 LIST_ENTRY KiStackInSwapListHead
;
23 KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable
[SSDT_MAX_ENTRIES
];
24 KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTableShadow
[SSDT_MAX_ENTRIES
];
26 /* FUNCTIONS *****************************************************************/
30 KeGetCurrentProcess(VOID
)
32 return(&(PsGetCurrentProcess()->Pcb
));
38 UpdatePageDirs(IN PKTHREAD Thread
,
42 * The stack and the thread structure of the current process may be
43 * located in a page which is not present in the page directory of
44 * the process we're attaching to. That would lead to a page fault
45 * when this function returns. However, since the processor can't
46 * call the page fault handler 'cause it can't push EIP on the stack,
47 * this will show up as a stack fault which will crash the entire system.
48 * To prevent this, make sure the page directory of the process we're
49 * attaching to is up-to-date.
51 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, KERNEL_STACK_SIZE
);
52 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
57 KiAttachProcess(PKTHREAD Thread
,
60 PRKAPC_STATE SavedApcState
)
62 ASSERT(Process
!= Thread
->ApcState
.Process
);
63 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n",
64 Thread
, Process
, SavedApcState
);
66 /* Increase Stack Count */
67 Process
->StackCount
++;
69 /* Swap the APC Environment */
70 KiMoveApcState(&Thread
->ApcState
, SavedApcState
);
72 /* Reinitialize Apc State */
73 InitializeListHead(&Thread
->ApcState
.ApcListHead
[KernelMode
]);
74 InitializeListHead(&Thread
->ApcState
.ApcListHead
[UserMode
]);
75 Thread
->ApcState
.Process
= Process
;
76 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
77 Thread
->ApcState
.KernelApcPending
= FALSE
;
78 Thread
->ApcState
.UserApcPending
= FALSE
;
80 /* Update Environment Pointers if needed*/
81 if (SavedApcState
== &Thread
->SavedApcState
)
83 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->SavedApcState
;
84 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->ApcState
;
85 Thread
->ApcStateIndex
= AttachedApcEnvironment
;
88 /* Check if the process is paged in */
89 if (Process
->State
== ProcessInMemory
)
91 /* FIXME: Scan the Ready Thread List once new scheduler is in */
93 /* Swap the Processes */
94 KiSwapProcess(Process
, SavedApcState
->Process
);
96 /* Return to old IRQL*/
97 KeReleaseDispatcherDatabaseLock(OldIrql
);
101 DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
108 KeInitializeProcess(IN OUT PKPROCESS Process
,
109 IN KPRIORITY Priority
,
110 IN KAFFINITY Affinity
,
111 IN LARGE_INTEGER DirectoryTableBase
)
117 /* Initialize the Dispatcher Header */
118 KeInitializeDispatcherHeader(&Process
->Header
,
123 /* Initialize Scheduler Data, Alignment Faults and Set the PDE */
124 Process
->Affinity
= Affinity
;
125 Process
->BasePriority
= (CHAR
)Priority
;
126 Process
->QuantumReset
= 6;
127 Process
->DirectoryTableBase
= DirectoryTableBase
;
128 Process
->AutoAlignment
= TRUE
;
129 Process
->IopmOffset
= 0xFFFF;
131 /* Initialize the lists */
132 InitializeListHead(&Process
->ThreadListHead
);
133 InitializeListHead(&Process
->ProfileListHead
);
134 InitializeListHead(&Process
->ReadyListHead
);
136 /* Initialize the current State */
137 Process
->State
= ProcessInMemory
;
139 /* Check how many Nodes there are on the system */
140 if (KeNumberNodes
> 1)
142 /* Set the new seed */
143 KeProcessNodeSeed
= (KeProcessNodeSeed
+ 1) / KeNumberNodes
;
144 IdealNode
= KeProcessNodeSeed
;
146 /* Loop every node */
149 /* Check if the affinity matches */
150 if (KeNodeBlock
[IdealNode
]->ProcessorMask
!= Affinity
) break;
152 /* No match, try next Ideal Node and increase node loop index */
156 /* Check if the Ideal Node is beyond the total number of nodes */
157 if (IdealNode
>= KeNumberNodes
)
159 /* Normalize the Ideal Node */
160 IdealNode
-= KeNumberNodes
;
162 } while (i
< KeNumberNodes
);
165 /* Set the ideal node and get the ideal node block */
166 Process
->IdealNode
= IdealNode
;
167 Node
= KeNodeBlock
[IdealNode
];
168 ASSERT(Node
->ProcessorMask
& Affinity
);
170 /* Find the matching affinity set to calculate the thread seed */
171 Affinity
&= Node
->ProcessorMask
;
172 Process
->ThreadSeed
= KeFindNextRightSetAffinity(Node
->Seed
,
174 Node
->Seed
= Process
->ThreadSeed
;
179 KeSetProcess(PKPROCESS Process
,
186 /* Lock Dispatcher */
187 OldIrql
= KeAcquireDispatcherDatabaseLock();
190 OldState
= Process
->Header
.SignalState
;
192 /* Signal the Process */
193 Process
->Header
.SignalState
= TRUE
;
194 if ((OldState
== 0) && IsListEmpty(&Process
->Header
.WaitListHead
) != TRUE
)
197 KiWaitTest((PVOID
)Process
, Increment
);
200 /* Release Dispatcher Database */
201 KeReleaseDispatcherDatabaseLock(OldIrql
);
203 /* Return the previous State */
209 KiSwapProcess(PKPROCESS NewProcess
,
210 PKPROCESS OldProcess
)
212 DPRINT("Switching CR3 to: %x\n", NewProcess
->DirectoryTableBase
.u
.LowPart
);
213 Ke386SetPageTableDirectory(NewProcess
->DirectoryTableBase
.u
.LowPart
);
218 KeSetQuantumProcess(IN PKPROCESS Process
,
222 PLIST_ENTRY NextEntry
, ListHead
;
224 ASSERT_PROCESS(Process
);
225 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
227 /* Lock Dispatcher */
228 OldIrql
= KeAcquireDispatcherDatabaseLock();
230 /* Set new quantum */
231 Process
->QuantumReset
= Quantum
;
233 /* Loop all child threads */
234 ListHead
= &Process
->ThreadListHead
;
235 NextEntry
= ListHead
->Flink
;
236 while (ListHead
!= NextEntry
)
239 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
242 Thread
->QuantumReset
= Quantum
;
244 /* Go to the next one */
245 NextEntry
= NextEntry
->Flink
;
248 /* Release Dispatcher Database */
249 KeReleaseDispatcherDatabaseLock(OldIrql
);
254 KeSetPriorityAndQuantumProcess(IN PKPROCESS Process
,
255 IN KPRIORITY Priority
,
256 IN UCHAR Quantum OPTIONAL
)
259 PLIST_ENTRY NextEntry
, ListHead
;
260 KPRIORITY NewPriority
, OldPriority
;
264 ASSERT_PROCESS(Process
);
265 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
267 /* Check if the process already has this priority */
268 if (Process
->BasePriority
== Priority
)
270 /* Don't change anything */
271 return Process
->BasePriority
;
274 /* If the caller gave priority 0, normalize to 1 */
275 if (!Priority
) Priority
= 1;
277 /* Lock Dispatcher */
278 OldIrql
= KeAcquireDispatcherDatabaseLock();
280 /* Check if we are modifying the quantum too */
281 if (Quantum
) Process
->QuantumReset
= Quantum
;
283 /* Save the current base priority and update it */
284 OldPriority
= Process
->BasePriority
;
285 Process
->BasePriority
= Priority
;
287 /* Calculate the priority delta */
288 Delta
= Priority
- OldPriority
;
290 /* Set the list head and list entry */
291 ListHead
= &Process
->ThreadListHead
;
292 NextEntry
= ListHead
->Flink
;
294 /* Check if this is a real-time priority */
295 if (Priority
>= LOW_REALTIME_PRIORITY
)
297 /* Loop the thread list */
298 while (NextEntry
!= ListHead
)
301 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
303 /* Update the quantum if we had one */
304 if (Quantum
) Thread
->QuantumReset
= Quantum
;
306 /* Calculate the new priority */
307 NewPriority
= Thread
->BasePriority
+ Delta
;
308 if (NewPriority
< LOW_REALTIME_PRIORITY
)
310 /* We're in real-time range, don't let it go below */
311 NewPriority
= LOW_REALTIME_PRIORITY
;
313 else if (NewPriority
> HIGH_PRIORITY
)
315 /* We're going beyond the maximum priority, normalize */
316 NewPriority
= HIGH_PRIORITY
;
320 * If priority saturation occured or the old priority was still in
321 * the real-time range, don't do anything.
323 if (!(Thread
->Saturation
) || (OldPriority
< LOW_REALTIME_PRIORITY
))
325 /* Check if we had priority saturation */
326 if (Thread
->Saturation
> 0)
328 /* Boost priority to maximum */
329 NewPriority
= HIGH_PRIORITY
;
331 else if (Thread
->Saturation
< 0)
333 /* If we had negative saturation, set minimum priority */
334 NewPriority
= LOW_REALTIME_PRIORITY
;
337 /* Update priority and quantum */
338 Thread
->BasePriority
= NewPriority
;
339 Thread
->Quantum
= Thread
->QuantumReset
;
341 /* Disable decrements and update priority */
342 Thread
->PriorityDecrement
= 0;
343 KiSetPriorityThread(Thread
, NewPriority
, &Released
);
346 /* Go to the next thread */
347 NextEntry
= NextEntry
->Flink
;
352 /* Loop the thread list */
353 while (NextEntry
!= ListHead
)
356 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
358 /* Update the quantum if we had one */
359 if (Quantum
) Thread
->QuantumReset
= Quantum
;
361 /* Calculate the new priority */
362 NewPriority
= Thread
->BasePriority
+ Delta
;
363 if (NewPriority
>= LOW_REALTIME_PRIORITY
)
365 /* We're not real-time range, don't let it enter RT range */
366 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
368 else if (NewPriority
<= LOW_PRIORITY
)
370 /* We're going below the minimum priority, normalize */
375 * If priority saturation occured or the old priority was still in
376 * the real-time range, don't do anything.
378 if (!(Thread
->Saturation
) ||
379 (OldPriority
>= LOW_REALTIME_PRIORITY
))
381 /* Check if we had priority saturation */
382 if (Thread
->Saturation
> 0)
384 /* Boost priority to maximum */
385 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
387 else if (Thread
->Saturation
< 0)
389 /* If we had negative saturation, set minimum priority */
393 /* Update priority and quantum */
394 Thread
->BasePriority
= NewPriority
;
395 Thread
->Quantum
= Thread
->QuantumReset
;
397 /* Disable decrements and update priority */
398 Thread
->PriorityDecrement
= 0;
399 KiSetPriorityThread(Thread
, NewPriority
, &Released
);
402 /* Go to the next thread */
403 NextEntry
= NextEntry
->Flink
;
407 /* Release Dispatcher Database */
408 if (!Released
) KeReleaseDispatcherDatabaseLock(OldIrql
);
410 /* Return previous priority */
419 KeAttachProcess(PKPROCESS Process
)
423 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
424 DPRINT("KeAttachProcess: %x\n", Process
);
426 /* Make sure that we are in the right page directory */
427 Thread
= KeGetCurrentThread();
428 UpdatePageDirs(Thread
, Process
);
430 /* Lock Dispatcher */
431 OldIrql
= KeAcquireDispatcherDatabaseLock();
433 /* Check if we're already in that process */
434 if (Thread
->ApcState
.Process
== Process
)
436 /* Unlock the dispatcher, nothing to do */
437 KeReleaseDispatcherDatabaseLock(OldIrql
);
439 else if ((Thread
->ApcStateIndex
!= OriginalApcEnvironment
) ||
440 (KeIsExecutingDpc()))
442 /* Executing a DPC or already attached, crash! */
443 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
445 (ULONG_PTR
)Thread
->ApcState
.Process
,
446 Thread
->ApcStateIndex
,
451 /* Legit attach attempt: do it! */
452 KiAttachProcess(Thread
, Process
, OldIrql
, &Thread
->SavedApcState
);
461 KeDetachProcess (VOID
)
465 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
466 DPRINT("KeDetachProcess()\n");
468 /* Get Current Thread and lock the dispatcher */
469 Thread
= KeGetCurrentThread();
470 OldIrql
= KeAcquireDispatcherDatabaseLock();
472 /* Check if it's attached */
473 if (Thread
->ApcStateIndex
!= OriginalApcEnvironment
)
475 /* It is, decrease Stack Count */
476 if(!(--Thread
->ApcState
.Process
->StackCount
))
478 /* FIXME: Swap the process out */
481 /* Restore the APC State */
482 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
483 Thread
->SavedApcState
.Process
= NULL
;
484 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
485 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
486 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
488 /* Check if we have pending APCs */
489 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
491 /* What do you know, we do! Request them to be delivered */
492 Thread
->ApcState
.KernelApcPending
= TRUE
;
493 HalRequestSoftwareInterrupt(APC_LEVEL
);
497 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
500 /* Unlock Dispatcher */
501 KeReleaseDispatcherDatabaseLock(OldIrql
);
509 KeIsAttachedProcess(VOID
)
511 /* Return the APC State */
512 return KeGetCurrentThread()->ApcStateIndex
;
520 KeStackAttachProcess(IN PKPROCESS Process
,
521 OUT PRKAPC_STATE ApcState
)
525 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
527 /* Make sure that we are in the right page directory */
528 Thread
= KeGetCurrentThread();
529 UpdatePageDirs(Thread
, Process
);
531 /* Acquire the dispatcher lock */
532 OldIrql
= KeAcquireDispatcherDatabaseLock();
534 /* Crash system if DPC is being executed! */
535 if (KeIsExecutingDpc())
537 /* Executing a DPC, crash! */
538 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
540 (ULONG_PTR
)Thread
->ApcState
.Process
,
541 Thread
->ApcStateIndex
,
545 /* Check if we are already in the target process */
546 if (Thread
->ApcState
.Process
== Process
)
548 /* Unlock the dispatcher database */
549 KeReleaseDispatcherDatabaseLock(OldIrql
);
551 /* Set magic value so we don't crash later when detaching */
552 ApcState
->Process
= (PKPROCESS
)1;
556 /* Check if the Current Thread is already attached */
557 if (Thread
->ApcStateIndex
!= OriginalApcEnvironment
)
559 /* We're already attached, so save the APC State into what we got */
560 KiAttachProcess(Thread
, Process
, OldIrql
, ApcState
);
564 /* We're not attached, so save the APC State into SavedApcState */
565 KiAttachProcess(Thread
, Process
, OldIrql
, &Thread
->SavedApcState
);
566 ApcState
->Process
= NULL
;
576 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState
)
580 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
582 /* Get the current thread and acquire the dispatcher lock */
583 Thread
= KeGetCurrentThread();
584 OldIrql
= KeAcquireDispatcherDatabaseLock();
586 /* Check for magic value meaning we were already in the same process */
587 if (ApcState
->Process
!= (PKPROCESS
)1)
590 * Check if the process isn't attacked, or has a Kernel APC in progress
591 * or has pending APC of any kind.
593 if ((Thread
->ApcStateIndex
== OriginalApcEnvironment
) ||
594 (Thread
->ApcState
.KernelApcInProgress
) ||
595 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) ||
596 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
598 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT
);
601 /* Decrease Stack Count */
602 if(!(--Thread
->ApcState
.Process
->StackCount
))
604 /* FIXME: Swap the process out */
607 if (ApcState
->Process
!= NULL
)
609 /* Restore the APC State */
610 KiMoveApcState(ApcState
, &Thread
->ApcState
);
614 /* The ApcState parameter is useless, so use the saved data and reset it */
615 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
616 Thread
->SavedApcState
.Process
= NULL
;
617 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
618 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
619 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
622 /* Check if we have pending APCs */
623 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
625 /* What do you know, we do! Request them to be delivered */
626 Thread
->ApcState
.KernelApcPending
= TRUE
;
627 HalRequestSoftwareInterrupt(APC_LEVEL
);
631 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
634 /* Return to old IRQL*/
635 KeReleaseDispatcherDatabaseLock(OldIrql
);
643 KeAddSystemServiceTable(PULONG_PTR Base
,
644 PULONG Count OPTIONAL
,
649 /* Check if descriptor table entry is free */
650 if ((Index
> SSDT_MAX_ENTRIES
- 1) ||
651 (KeServiceDescriptorTable
[Index
].Base
) ||
652 (KeServiceDescriptorTableShadow
[Index
].Base
))
657 /* Initialize the shadow service descriptor table */
658 KeServiceDescriptorTableShadow
[Index
].Base
= Base
;
659 KeServiceDescriptorTableShadow
[Index
].Limit
= Limit
;
660 KeServiceDescriptorTableShadow
[Index
].Number
= Number
;
661 KeServiceDescriptorTableShadow
[Index
].Count
= Count
;
671 KeRemoveSystemServiceTable(IN ULONG Index
)
673 /* Make sure the Index is valid */
674 if (Index
> SSDT_MAX_ENTRIES
- 1) return FALSE
;
676 /* Is there a Normal Descriptor Table? */
677 if (!KeServiceDescriptorTable
[Index
].Base
)
679 /* Not with the index, is there a shadow at least? */
680 if (!KeServiceDescriptorTableShadow
[Index
].Base
) return FALSE
;
683 /* Now clear from the Shadow Table. */
684 KeServiceDescriptorTableShadow
[Index
].Base
= NULL
;
685 KeServiceDescriptorTableShadow
[Index
].Number
= NULL
;
686 KeServiceDescriptorTableShadow
[Index
].Limit
= 0;
687 KeServiceDescriptorTableShadow
[Index
].Count
= NULL
;
689 /* Check if we should clean from the Master one too */
692 KeServiceDescriptorTable
[Index
].Base
= NULL
;
693 KeServiceDescriptorTable
[Index
].Number
= NULL
;
694 KeServiceDescriptorTable
[Index
].Limit
= 0;
695 KeServiceDescriptorTable
[Index
].Count
= NULL
;