2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
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 PVOID KeUserApcDispatcher
;
27 PVOID KeUserCallbackDispatcher
;
28 PVOID KeUserExceptionDispatcher
;
29 PVOID KeRaiseUserExceptionDispatcher
;
31 /* PRIVATE FUNCTIONS *********************************************************/
35 KiAttachProcess(IN PKTHREAD Thread
,
37 IN PKLOCK_QUEUE_HANDLE ApcLock
,
38 IN PRKAPC_STATE SavedApcState
)
41 PLIST_ENTRY ListHead
, NextEntry
;
42 PKTHREAD CurrentThread
;
44 ASSERT(Process
!= Thread
->ApcState
.Process
);
46 /* Increase Stack Count */
47 ASSERT(Process
->StackCount
!= MAXULONG_PTR
);
48 Process
->StackCount
++;
50 /* Swap the APC Environment */
51 KiMoveApcState(&Thread
->ApcState
, SavedApcState
);
53 /* Reinitialize Apc State */
54 InitializeListHead(&Thread
->ApcState
.ApcListHead
[KernelMode
]);
55 InitializeListHead(&Thread
->ApcState
.ApcListHead
[UserMode
]);
56 Thread
->ApcState
.Process
= Process
;
57 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
58 Thread
->ApcState
.KernelApcPending
= FALSE
;
59 Thread
->ApcState
.UserApcPending
= FALSE
;
61 /* Update Environment Pointers if needed*/
62 if (SavedApcState
== &Thread
->SavedApcState
)
64 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->
66 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->ApcState
;
67 Thread
->ApcStateIndex
= AttachedApcEnvironment
;
70 /* Check if the process is paged in */
71 if (Process
->State
== ProcessInMemory
)
73 /* Scan the ready list */
75 ListHead
= &Process
->ReadyListHead
;
76 NextEntry
= ListHead
->Flink
;
77 while (NextEntry
!= ListHead
)
80 CurrentThread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, WaitListEntry
);
83 RemoveEntryList(NextEntry
);
84 CurrentThread
->ProcessReadyQueue
= FALSE
;
87 KiReadyThread(CurrentThread
);
89 /* Go to the next one */
90 NextEntry
= ListHead
->Flink
;
94 /* Release dispatcher lock */
95 KiReleaseDispatcherLockFromDpcLevel();
98 KiReleaseApcLockFromDpcLevel(ApcLock
);
101 KiSwapProcess(Process
, SavedApcState
->Process
);
103 /* Exit the dispatcher */
104 KiExitDispatcher(ApcLock
->OldIrql
);
108 DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
115 KeInitializeProcess(IN OUT PKPROCESS Process
,
116 IN KPRIORITY Priority
,
117 IN KAFFINITY Affinity
,
118 IN PULONG DirectoryTableBase
,
127 /* Initialize the Dispatcher Header */
128 KeInitializeDispatcherHeader(&Process
->Header
,
133 /* Initialize Scheduler Data, Alignment Faults and Set the PDE */
134 Process
->Affinity
= Affinity
;
135 Process
->BasePriority
= (CHAR
)Priority
;
136 Process
->QuantumReset
= 6;
137 Process
->DirectoryTableBase
[0] = DirectoryTableBase
[0];
138 Process
->DirectoryTableBase
[1] = DirectoryTableBase
[1];
139 Process
->AutoAlignment
= Enable
;
141 Process
->IopmOffset
= KiComputeIopmOffset(IO_ACCESS_MAP_NONE
);
144 /* Initialize the lists */
145 InitializeListHead(&Process
->ThreadListHead
);
146 InitializeListHead(&Process
->ProfileListHead
);
147 InitializeListHead(&Process
->ReadyListHead
);
149 /* Initialize the current State */
150 Process
->State
= ProcessInMemory
;
152 /* Check how many Nodes there are on the system */
154 if (KeNumberNodes
> 1)
156 /* Set the new seed */
157 KeProcessNodeSeed
= (KeProcessNodeSeed
+ 1) / KeNumberNodes
;
158 IdealNode
= KeProcessNodeSeed
;
160 /* Loop every node */
163 /* Check if the affinity matches */
164 if (KeNodeBlock
[IdealNode
]->ProcessorMask
!= Affinity
) break;
166 /* No match, try next Ideal Node and increase node loop index */
170 /* Check if the Ideal Node is beyond the total number of nodes */
171 if (IdealNode
>= KeNumberNodes
)
173 /* Normalize the Ideal Node */
174 IdealNode
-= KeNumberNodes
;
176 } while (i
< KeNumberNodes
);
179 /* Set the ideal node and get the ideal node block */
180 Process
->IdealNode
= IdealNode
;
181 Node
= KeNodeBlock
[IdealNode
];
182 ASSERT(Node
->ProcessorMask
& Affinity
);
184 /* Find the matching affinity set to calculate the thread seed */
185 Affinity
&= Node
->ProcessorMask
;
186 Process
->ThreadSeed
= KeFindNextRightSetAffinity(Node
->Seed
,
188 Node
->Seed
= Process
->ThreadSeed
;
194 KeSetProcess(IN PKPROCESS Process
,
195 IN KPRIORITY Increment
,
200 ASSERT_PROCESS(Process
);
201 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
203 /* Lock Dispatcher */
204 OldIrql
= KiAcquireDispatcherLock();
207 OldState
= Process
->Header
.SignalState
;
209 /* Signal the Process */
210 Process
->Header
.SignalState
= TRUE
;
212 /* Check if was unsignaled and has waiters */
214 !(IsListEmpty(&Process
->Header
.WaitListHead
)))
216 /* Unwait the threads */
217 KxUnwaitThread(&Process
->Header
, Increment
);
220 /* Release Dispatcher Database */
221 KiReleaseDispatcherLock(OldIrql
);
223 /* Return the previous State */
229 KeSetQuantumProcess(IN PKPROCESS Process
,
232 KLOCK_QUEUE_HANDLE ProcessLock
;
233 PLIST_ENTRY NextEntry
, ListHead
;
235 ASSERT_PROCESS(Process
);
236 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
238 /* Lock the process */
239 KiAcquireProcessLock(Process
, &ProcessLock
);
241 /* Set new quantum */
242 Process
->QuantumReset
= Quantum
;
244 /* Loop all child threads */
245 ListHead
= &Process
->ThreadListHead
;
246 NextEntry
= ListHead
->Flink
;
247 while (ListHead
!= NextEntry
)
250 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
253 Thread
->QuantumReset
= Quantum
;
255 /* Go to the next one */
256 NextEntry
= NextEntry
->Flink
;
260 KiReleaseProcessLock(&ProcessLock
);
265 KeSetPriorityAndQuantumProcess(IN PKPROCESS Process
,
266 IN KPRIORITY Priority
,
267 IN UCHAR Quantum OPTIONAL
)
269 KLOCK_QUEUE_HANDLE ProcessLock
;
271 PLIST_ENTRY NextEntry
, ListHead
;
272 KPRIORITY NewPriority
, OldPriority
;
274 ASSERT_PROCESS(Process
);
275 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
277 /* Check if the process already has this priority */
278 if (Process
->BasePriority
== Priority
) return Process
->BasePriority
;
280 /* If the caller gave priority 0, normalize to 1 */
281 if (!LOW_PRIORITY
) Priority
= LOW_PRIORITY
+ 1;
283 /* Lock the process */
284 KiAcquireProcessLock(Process
, &ProcessLock
);
286 /* Check if we are modifying the quantum too */
287 if (Quantum
) Process
->QuantumReset
= Quantum
;
289 /* Save the current base priority and update it */
290 OldPriority
= Process
->BasePriority
;
291 Process
->BasePriority
= (SCHAR
)Priority
;
293 /* Calculate the priority delta */
294 Delta
= Priority
- OldPriority
;
296 /* Set the list head and list entry */
297 ListHead
= &Process
->ThreadListHead
;
298 NextEntry
= ListHead
->Flink
;
300 /* Check if this is a real-time priority */
301 if (Priority
>= LOW_REALTIME_PRIORITY
)
303 /* Loop the thread list */
304 while (NextEntry
!= ListHead
)
307 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
309 /* Update the quantum if we had one */
310 if (Quantum
) Thread
->QuantumReset
= Quantum
;
312 /* Acquire the thread lock */
313 KiAcquireThreadLock(Thread
);
315 /* Calculate the new priority */
316 NewPriority
= Thread
->BasePriority
+ Delta
;
317 if (NewPriority
< LOW_REALTIME_PRIORITY
)
319 /* We're in real-time range, don't let it go below */
320 NewPriority
= LOW_REALTIME_PRIORITY
;
322 else if (NewPriority
> HIGH_PRIORITY
)
324 /* We're going beyond the maximum priority, normalize */
325 NewPriority
= HIGH_PRIORITY
;
329 * If priority saturation occured or the old priority was still in
330 * the real-time range, don't do anything.
332 if (!(Thread
->Saturation
) || (OldPriority
< LOW_REALTIME_PRIORITY
))
334 /* Check if we had priority saturation */
335 if (Thread
->Saturation
> 0)
337 /* Boost priority to maximum */
338 NewPriority
= HIGH_PRIORITY
;
340 else if (Thread
->Saturation
< 0)
342 /* If we had negative saturation, set minimum priority */
343 NewPriority
= LOW_REALTIME_PRIORITY
;
346 /* Update priority and quantum */
347 Thread
->BasePriority
= (SCHAR
)NewPriority
;
348 Thread
->Quantum
= Thread
->QuantumReset
;
350 /* Disable decrements and update priority */
351 Thread
->PriorityDecrement
= 0;
352 KiSetPriorityThread(Thread
, NewPriority
);
355 /* Release the thread lock */
356 KiReleaseThreadLock(Thread
);
358 /* Go to the next thread */
359 NextEntry
= NextEntry
->Flink
;
364 /* Loop the thread list */
365 while (NextEntry
!= ListHead
)
368 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
370 /* Update the quantum if we had one */
371 if (Quantum
) Thread
->QuantumReset
= Quantum
;
373 /* Lock the thread */
374 KiAcquireThreadLock(Thread
);
376 /* Calculate the new priority */
377 NewPriority
= Thread
->BasePriority
+ Delta
;
378 if (NewPriority
>= LOW_REALTIME_PRIORITY
)
380 /* We're not real-time range, don't let it enter RT range */
381 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
383 else if (NewPriority
<= LOW_PRIORITY
)
385 /* We're going below the minimum priority, normalize */
390 * If priority saturation occured or the old priority was still in
391 * the real-time range, don't do anything.
393 if (!(Thread
->Saturation
) ||
394 (OldPriority
>= LOW_REALTIME_PRIORITY
))
396 /* Check if we had priority saturation */
397 if (Thread
->Saturation
> 0)
399 /* Boost priority to maximum */
400 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
402 else if (Thread
->Saturation
< 0)
404 /* If we had negative saturation, set minimum priority */
408 /* Update priority and quantum */
409 Thread
->BasePriority
= (SCHAR
)NewPriority
;
410 Thread
->Quantum
= Thread
->QuantumReset
;
412 /* Disable decrements and update priority */
413 Thread
->PriorityDecrement
= 0;
414 KiSetPriorityThread(Thread
, NewPriority
);
417 /* Release the thread lock */
418 KiReleaseThreadLock(Thread
);
420 /* Go to the next thread */
421 NextEntry
= NextEntry
->Flink
;
425 /* Release Dispatcher Database */
426 KiReleaseDispatcherLockFromDpcLevel();
428 /* Release the process lock */
429 KiReleaseProcessLockFromDpcLevel(&ProcessLock
);
430 KiExitDispatcher(ProcessLock
.OldIrql
);
432 /* Return previous priority */
436 /* PUBLIC FUNCTIONS **********************************************************/
443 KeAttachProcess(IN PKPROCESS Process
)
445 KLOCK_QUEUE_HANDLE ApcLock
;
446 PKTHREAD Thread
= KeGetCurrentThread();
447 ASSERT_PROCESS(Process
);
448 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
450 /* Make sure that we are in the right page directory (ReactOS Mm Hack) */
451 MiSyncThreadProcessViews(Thread
);
453 /* Check if we're already in that process */
454 if (Thread
->ApcState
.Process
== Process
) return;
456 /* Check if a DPC is executing or if we're already attached */
457 if ((Thread
->ApcStateIndex
!= OriginalApcEnvironment
) ||
458 (KeIsExecutingDpc()))
460 /* Invalid attempt */
461 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
463 (ULONG_PTR
)Thread
->ApcState
.Process
,
464 Thread
->ApcStateIndex
,
469 /* Acquire APC Lock */
470 KiAcquireApcLock(Thread
, &ApcLock
);
472 /* Acquire the dispatcher lock */
473 KiAcquireDispatcherLockAtDpcLevel();
475 /* Legit attach attempt: do it! */
476 KiAttachProcess(Thread
, Process
, &ApcLock
, &Thread
->SavedApcState
);
485 KeDetachProcess(VOID
)
487 PKTHREAD Thread
= KeGetCurrentThread();
488 KLOCK_QUEUE_HANDLE ApcLock
;
490 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
492 /* Check if it's attached */
493 if (Thread
->ApcStateIndex
== OriginalApcEnvironment
) return;
495 /* Acquire APC Lock */
496 KiAcquireApcLock(Thread
, &ApcLock
);
498 /* Check for invalid attach attempts */
499 if ((Thread
->ApcState
.KernelApcInProgress
) ||
500 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) ||
501 !(IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
503 /* Crash the system */
504 KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT
);
507 /* Get the process */
508 Process
= Thread
->ApcState
.Process
;
510 /* Acquire dispatcher lock */
511 KiAcquireDispatcherLockAtDpcLevel();
513 /* Decrease the stack count */
514 ASSERT(Process
->StackCount
!= 0);
515 ASSERT(Process
->State
== ProcessInMemory
);
516 Process
->StackCount
--;
518 /* Check if we can swap the process out */
519 if (!Process
->StackCount
)
521 /* FIXME: Swap the process out */
524 /* Release dispatcher lock */
525 KiReleaseDispatcherLockFromDpcLevel();
527 /* Restore the APC State */
528 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
529 Thread
->SavedApcState
.Process
= NULL
;
530 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
531 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
532 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
535 KiReleaseApcLockFromDpcLevel(&ApcLock
);
538 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
540 /* Exit the dispatcher */
541 KiExitDispatcher(ApcLock
.OldIrql
);
543 /* Check if we have pending APCs */
544 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
546 /* What do you know, we do! Request them to be delivered */
547 Thread
->ApcState
.KernelApcPending
= TRUE
;
548 HalRequestSoftwareInterrupt(APC_LEVEL
);
557 KeIsAttachedProcess(VOID
)
559 /* Return the APC State */
560 return KeGetCurrentThread()->ApcStateIndex
;
568 KeStackAttachProcess(IN PKPROCESS Process
,
569 OUT PRKAPC_STATE ApcState
)
571 KLOCK_QUEUE_HANDLE ApcLock
;
572 PKTHREAD Thread
= KeGetCurrentThread();
573 ASSERT_PROCESS(Process
);
574 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
576 /* Make sure that we are in the right page directory (ReactOS Mm Hack) */
577 MiSyncThreadProcessViews(Thread
);
579 /* Crash system if DPC is being executed! */
580 if (KeIsExecutingDpc())
582 /* Executing a DPC, crash! */
583 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
585 (ULONG_PTR
)Thread
->ApcState
.Process
,
586 Thread
->ApcStateIndex
,
590 /* Check if we are already in the target process */
591 if (Thread
->ApcState
.Process
== Process
)
593 /* Set magic value so we don't crash later when detaching */
594 ApcState
->Process
= (PKPROCESS
)1;
598 /* Acquire APC Lock */
599 KiAcquireApcLock(Thread
, &ApcLock
);
601 /* Acquire dispatcher lock */
602 KiAcquireDispatcherLockAtDpcLevel();
604 /* Check if the Current Thread is already attached */
605 if (Thread
->ApcStateIndex
!= OriginalApcEnvironment
)
607 /* We're already attached, so save the APC State into what we got */
608 KiAttachProcess(Thread
, Process
, &ApcLock
, ApcState
);
612 /* We're not attached, so save the APC State into SavedApcState */
613 KiAttachProcess(Thread
, Process
, &ApcLock
, &Thread
->SavedApcState
);
614 ApcState
->Process
= NULL
;
623 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState
)
625 KLOCK_QUEUE_HANDLE ApcLock
;
626 PKTHREAD Thread
= KeGetCurrentThread();
628 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
630 /* Check for magic value meaning we were already in the same process */
631 if (ApcState
->Process
== (PKPROCESS
)1) return;
633 /* Loop to make sure no APCs are pending */
636 /* Acquire APC Lock */
637 KiAcquireApcLock(Thread
, &ApcLock
);
639 /* Check if a kernel APC is pending */
640 if (Thread
->ApcState
.KernelApcPending
)
642 /* Check if kernel APC should be delivered */
643 if (!(Thread
->KernelApcDisable
) && (ApcLock
.OldIrql
<= APC_LEVEL
))
645 /* Release the APC lock so that the APC can be delivered */
646 KiReleaseApcLock(&ApcLock
);
651 /* Otherwise, break out */
656 * Check if the process isn't attacked, or has a Kernel APC in progress
657 * or has pending APC of any kind.
659 if ((Thread
->ApcStateIndex
== OriginalApcEnvironment
) ||
660 (Thread
->ApcState
.KernelApcInProgress
) ||
661 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) ||
662 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
664 /* Bugcheck the system */
665 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT
);
668 /* Get the process */
669 Process
= Thread
->ApcState
.Process
;
671 /* Acquire dispatcher lock */
672 KiAcquireDispatcherLockAtDpcLevel();
674 /* Decrease the stack count */
675 ASSERT(Process
->StackCount
!= 0);
676 ASSERT(Process
->State
== ProcessInMemory
);
677 Process
->StackCount
--;
679 /* Check if we can swap the process out */
680 if (!Process
->StackCount
)
682 /* FIXME: Swap the process out */
685 /* Release dispatcher lock */
686 KiReleaseDispatcherLockFromDpcLevel();
688 /* Check if there's an APC state to restore */
689 if (ApcState
->Process
)
691 /* Restore the APC State */
692 KiMoveApcState(ApcState
, &Thread
->ApcState
);
696 /* The ApcState parameter is useless, so use the saved data and reset it */
697 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
698 Thread
->SavedApcState
.Process
= NULL
;
699 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
700 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
701 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
705 KiReleaseApcLockFromDpcLevel(&ApcLock
);
708 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
710 /* Exit the dispatcher */
711 KiExitDispatcher(ApcLock
.OldIrql
);
713 /* Check if we have pending APCs */
714 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
716 /* What do you know, we do! Request them to be delivered */
717 Thread
->ApcState
.KernelApcPending
= TRUE
;
718 HalRequestSoftwareInterrupt(APC_LEVEL
);
727 KeAddSystemServiceTable(IN PULONG_PTR Base
,
728 IN PULONG Count OPTIONAL
,
735 /* Check if descriptor table entry is free */
736 if ((Index
> SSDT_MAX_ENTRIES
- 1) ||
737 (KeServiceDescriptorTable
[Index
].Base
) ||
738 (KeServiceDescriptorTableShadow
[Index
].Base
))
744 /* Initialize the shadow service descriptor table */
745 KeServiceDescriptorTableShadow
[Index
].Base
= Base
;
746 KeServiceDescriptorTableShadow
[Index
].Limit
= Limit
;
747 KeServiceDescriptorTableShadow
[Index
].Number
= Number
;
748 KeServiceDescriptorTableShadow
[Index
].Count
= Count
;
757 KeRemoveSystemServiceTable(IN ULONG Index
)
761 /* Make sure the Index is valid */
762 if (Index
> (SSDT_MAX_ENTRIES
- 1)) return FALSE
;
764 /* Is there a Normal Descriptor Table? */
765 if (!KeServiceDescriptorTable
[Index
].Base
)
767 /* Not with the index, is there a shadow at least? */
768 if (!KeServiceDescriptorTableShadow
[Index
].Base
) return FALSE
;
771 /* Now clear from the Shadow Table. */
772 KeServiceDescriptorTableShadow
[Index
].Base
= NULL
;
773 KeServiceDescriptorTableShadow
[Index
].Number
= NULL
;
774 KeServiceDescriptorTableShadow
[Index
].Limit
= 0;
775 KeServiceDescriptorTableShadow
[Index
].Count
= NULL
;
777 /* Check if we should clean from the Master one too */
780 KeServiceDescriptorTable
[Index
].Base
= NULL
;
781 KeServiceDescriptorTable
[Index
].Number
= NULL
;
782 KeServiceDescriptorTable
[Index
].Limit
= 0;
783 KeServiceDescriptorTable
[Index
].Count
= NULL
;