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 PVOID KeUserApcDispatcher
;
27 PVOID KeUserCallbackDispatcher
;
28 PVOID KeUserExceptionDispatcher
;
29 PVOID KeRaiseUserExceptionDispatcher
;
31 /* FUNCTIONS *****************************************************************/
35 KeGetCurrentProcess(VOID
)
37 return(&(PsGetCurrentProcess()->Pcb
));
43 UpdatePageDirs(IN PKTHREAD Thread
,
47 * The stack and the thread structure of the current process may be
48 * located in a page which is not present in the page directory of
49 * the process we're attaching to. That would lead to a page fault
50 * when this function returns. However, since the processor can't
51 * call the page fault handler 'cause it can't push EIP on the stack,
52 * this will show up as a stack fault which will crash the entire system.
53 * To prevent this, make sure the page directory of the process we're
54 * attaching to is up-to-date.
56 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
->StackLimit
, KERNEL_STACK_SIZE
);
57 MmUpdatePageDir((PEPROCESS
)Process
, (PVOID
)Thread
, sizeof(ETHREAD
));
62 KiAttachProcess(PKTHREAD Thread
,
65 PRKAPC_STATE SavedApcState
)
67 ASSERT(Process
!= Thread
->ApcState
.Process
);
68 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n",
69 Thread
, Process
, SavedApcState
);
71 /* Increase Stack Count */
72 Process
->StackCount
++;
74 /* Swap the APC Environment */
75 KiMoveApcState(&Thread
->ApcState
, SavedApcState
);
77 /* Reinitialize Apc State */
78 InitializeListHead(&Thread
->ApcState
.ApcListHead
[KernelMode
]);
79 InitializeListHead(&Thread
->ApcState
.ApcListHead
[UserMode
]);
80 Thread
->ApcState
.Process
= Process
;
81 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
82 Thread
->ApcState
.KernelApcPending
= FALSE
;
83 Thread
->ApcState
.UserApcPending
= FALSE
;
85 /* Update Environment Pointers if needed*/
86 if (SavedApcState
== &Thread
->SavedApcState
)
88 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->SavedApcState
;
89 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->ApcState
;
90 Thread
->ApcStateIndex
= AttachedApcEnvironment
;
93 /* Check if the process is paged in */
94 if (Process
->State
== ProcessInMemory
)
96 /* FIXME: Scan the Ready Thread List once new scheduler is in */
98 /* Swap the Processes */
99 KiSwapProcess(Process
, SavedApcState
->Process
);
101 /* Return to old IRQL*/
102 KeReleaseDispatcherDatabaseLock(OldIrql
);
106 DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
113 KeInitializeProcess(IN OUT PKPROCESS Process
,
114 IN KPRIORITY Priority
,
115 IN KAFFINITY Affinity
,
116 IN LARGE_INTEGER DirectoryTableBase
)
122 /* Initialize the Dispatcher Header */
123 KeInitializeDispatcherHeader(&Process
->Header
,
128 /* Initialize Scheduler Data, Alignment Faults and Set the PDE */
129 Process
->Affinity
= Affinity
;
130 Process
->BasePriority
= (CHAR
)Priority
;
131 Process
->QuantumReset
= 6;
132 Process
->DirectoryTableBase
= DirectoryTableBase
;
133 Process
->AutoAlignment
= TRUE
;
135 Process
->IopmOffset
= 0xFFFF;
138 /* Initialize the lists */
139 InitializeListHead(&Process
->ThreadListHead
);
140 InitializeListHead(&Process
->ProfileListHead
);
141 InitializeListHead(&Process
->ReadyListHead
);
143 /* Initialize the current State */
144 Process
->State
= ProcessInMemory
;
146 /* Check how many Nodes there are on the system */
147 if (KeNumberNodes
> 1)
149 /* Set the new seed */
150 KeProcessNodeSeed
= (KeProcessNodeSeed
+ 1) / KeNumberNodes
;
151 IdealNode
= KeProcessNodeSeed
;
153 /* Loop every node */
156 /* Check if the affinity matches */
157 if (KeNodeBlock
[IdealNode
]->ProcessorMask
!= Affinity
) break;
159 /* No match, try next Ideal Node and increase node loop index */
163 /* Check if the Ideal Node is beyond the total number of nodes */
164 if (IdealNode
>= KeNumberNodes
)
166 /* Normalize the Ideal Node */
167 IdealNode
-= KeNumberNodes
;
169 } while (i
< KeNumberNodes
);
172 /* Set the ideal node and get the ideal node block */
173 Process
->IdealNode
= IdealNode
;
174 Node
= KeNodeBlock
[IdealNode
];
175 ASSERT(Node
->ProcessorMask
& Affinity
);
177 /* Find the matching affinity set to calculate the thread seed */
178 Affinity
&= Node
->ProcessorMask
;
179 Process
->ThreadSeed
= KeFindNextRightSetAffinity(Node
->Seed
,
181 Node
->Seed
= Process
->ThreadSeed
;
186 KeSetProcess(PKPROCESS Process
,
193 /* Lock Dispatcher */
194 OldIrql
= KeAcquireDispatcherDatabaseLock();
197 OldState
= Process
->Header
.SignalState
;
199 /* Signal the Process */
200 Process
->Header
.SignalState
= TRUE
;
201 if ((OldState
== 0) && IsListEmpty(&Process
->Header
.WaitListHead
) != TRUE
)
204 KiWaitTest((PVOID
)Process
, Increment
);
207 /* Release Dispatcher Database */
208 KeReleaseDispatcherDatabaseLock(OldIrql
);
210 /* Return the previous State */
216 KiSwapProcess(PKPROCESS NewProcess
,
217 PKPROCESS OldProcess
)
220 DPRINT("Switching CR3 to: %x\n", NewProcess
->DirectoryTableBase
.u
.LowPart
);
221 Ke386SetPageTableDirectory(NewProcess
->DirectoryTableBase
.u
.LowPart
);
227 KeSetQuantumProcess(IN PKPROCESS Process
,
231 PLIST_ENTRY NextEntry
, ListHead
;
233 ASSERT_PROCESS(Process
);
234 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
236 /* Lock Dispatcher */
237 OldIrql
= KeAcquireDispatcherDatabaseLock();
239 /* Set new quantum */
240 Process
->QuantumReset
= Quantum
;
242 /* Loop all child threads */
243 ListHead
= &Process
->ThreadListHead
;
244 NextEntry
= ListHead
->Flink
;
245 while (ListHead
!= NextEntry
)
248 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
251 Thread
->QuantumReset
= Quantum
;
253 /* Go to the next one */
254 NextEntry
= NextEntry
->Flink
;
257 /* Release Dispatcher Database */
258 KeReleaseDispatcherDatabaseLock(OldIrql
);
263 KeSetPriorityAndQuantumProcess(IN PKPROCESS Process
,
264 IN KPRIORITY Priority
,
265 IN UCHAR Quantum OPTIONAL
)
268 PLIST_ENTRY NextEntry
, ListHead
;
269 KPRIORITY NewPriority
, OldPriority
;
273 ASSERT_PROCESS(Process
);
274 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
276 /* Check if the process already has this priority */
277 if (Process
->BasePriority
== Priority
)
279 /* Don't change anything */
280 return Process
->BasePriority
;
283 /* If the caller gave priority 0, normalize to 1 */
284 if (!Priority
) Priority
= 1;
286 /* Lock Dispatcher */
287 OldIrql
= KeAcquireDispatcherDatabaseLock();
289 /* Check if we are modifying the quantum too */
290 if (Quantum
) Process
->QuantumReset
= Quantum
;
292 /* Save the current base priority and update it */
293 OldPriority
= Process
->BasePriority
;
294 Process
->BasePriority
= Priority
;
296 /* Calculate the priority delta */
297 Delta
= Priority
- OldPriority
;
299 /* Set the list head and list entry */
300 ListHead
= &Process
->ThreadListHead
;
301 NextEntry
= ListHead
->Flink
;
303 /* Check if this is a real-time priority */
304 if (Priority
>= LOW_REALTIME_PRIORITY
)
306 /* Loop the thread list */
307 while (NextEntry
!= ListHead
)
310 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
312 /* Update the quantum if we had one */
313 if (Quantum
) Thread
->QuantumReset
= Quantum
;
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
= NewPriority
;
348 Thread
->Quantum
= Thread
->QuantumReset
;
350 /* Disable decrements and update priority */
351 Thread
->PriorityDecrement
= 0;
352 KiSetPriorityThread(Thread
, NewPriority
, &Released
);
355 /* Go to the next thread */
356 NextEntry
= NextEntry
->Flink
;
361 /* Loop the thread list */
362 while (NextEntry
!= ListHead
)
365 Thread
= CONTAINING_RECORD(NextEntry
, KTHREAD
, ThreadListEntry
);
367 /* Update the quantum if we had one */
368 if (Quantum
) Thread
->QuantumReset
= Quantum
;
370 /* Calculate the new priority */
371 NewPriority
= Thread
->BasePriority
+ Delta
;
372 if (NewPriority
>= LOW_REALTIME_PRIORITY
)
374 /* We're not real-time range, don't let it enter RT range */
375 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
377 else if (NewPriority
<= LOW_PRIORITY
)
379 /* We're going below the minimum priority, normalize */
384 * If priority saturation occured or the old priority was still in
385 * the real-time range, don't do anything.
387 if (!(Thread
->Saturation
) ||
388 (OldPriority
>= LOW_REALTIME_PRIORITY
))
390 /* Check if we had priority saturation */
391 if (Thread
->Saturation
> 0)
393 /* Boost priority to maximum */
394 NewPriority
= LOW_REALTIME_PRIORITY
- 1;
396 else if (Thread
->Saturation
< 0)
398 /* If we had negative saturation, set minimum priority */
402 /* Update priority and quantum */
403 Thread
->BasePriority
= NewPriority
;
404 Thread
->Quantum
= Thread
->QuantumReset
;
406 /* Disable decrements and update priority */
407 Thread
->PriorityDecrement
= 0;
408 KiSetPriorityThread(Thread
, NewPriority
, &Released
);
411 /* Go to the next thread */
412 NextEntry
= NextEntry
->Flink
;
416 /* Release Dispatcher Database */
417 if (!Released
) KeReleaseDispatcherDatabaseLock(OldIrql
);
419 /* Return previous priority */
428 KeAttachProcess(PKPROCESS Process
)
432 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
433 DPRINT("KeAttachProcess: %x\n", Process
);
435 /* Make sure that we are in the right page directory */
436 Thread
= KeGetCurrentThread();
437 UpdatePageDirs(Thread
, Process
);
439 /* Lock Dispatcher */
440 OldIrql
= KeAcquireDispatcherDatabaseLock();
442 /* Check if we're already in that process */
443 if (Thread
->ApcState
.Process
== Process
)
445 /* Unlock the dispatcher, nothing to do */
446 KeReleaseDispatcherDatabaseLock(OldIrql
);
448 else if ((Thread
->ApcStateIndex
!= OriginalApcEnvironment
) ||
449 (KeIsExecutingDpc()))
451 /* Executing a DPC or already attached, crash! */
452 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
454 (ULONG_PTR
)Thread
->ApcState
.Process
,
455 Thread
->ApcStateIndex
,
460 /* Legit attach attempt: do it! */
461 KiAttachProcess(Thread
, Process
, OldIrql
, &Thread
->SavedApcState
);
470 KeDetachProcess (VOID
)
474 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
475 DPRINT("KeDetachProcess()\n");
477 /* Get Current Thread and lock the dispatcher */
478 Thread
= KeGetCurrentThread();
479 OldIrql
= KeAcquireDispatcherDatabaseLock();
481 /* Check if it's attached */
482 if (Thread
->ApcStateIndex
!= OriginalApcEnvironment
)
484 /* It is, decrease Stack Count */
485 if(!(--Thread
->ApcState
.Process
->StackCount
))
487 /* FIXME: Swap the process out */
490 /* Restore the APC State */
491 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
492 Thread
->SavedApcState
.Process
= NULL
;
493 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
494 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
495 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
497 /* Check if we have pending APCs */
498 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
500 /* What do you know, we do! Request them to be delivered */
501 Thread
->ApcState
.KernelApcPending
= TRUE
;
502 HalRequestSoftwareInterrupt(APC_LEVEL
);
506 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
509 /* Unlock Dispatcher */
510 KeReleaseDispatcherDatabaseLock(OldIrql
);
518 KeIsAttachedProcess(VOID
)
520 /* Return the APC State */
521 return KeGetCurrentThread()->ApcStateIndex
;
529 KeStackAttachProcess(IN PKPROCESS Process
,
530 OUT PRKAPC_STATE ApcState
)
534 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
536 /* Make sure that we are in the right page directory */
537 Thread
= KeGetCurrentThread();
538 UpdatePageDirs(Thread
, Process
);
540 /* Acquire the dispatcher lock */
541 OldIrql
= KeAcquireDispatcherDatabaseLock();
543 /* Crash system if DPC is being executed! */
544 if (KeIsExecutingDpc())
546 /* Executing a DPC, crash! */
547 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
549 (ULONG_PTR
)Thread
->ApcState
.Process
,
550 Thread
->ApcStateIndex
,
554 /* Check if we are already in the target process */
555 if (Thread
->ApcState
.Process
== Process
)
557 /* Unlock the dispatcher database */
558 KeReleaseDispatcherDatabaseLock(OldIrql
);
560 /* Set magic value so we don't crash later when detaching */
561 ApcState
->Process
= (PKPROCESS
)1;
565 /* Check if the Current Thread is already attached */
566 if (Thread
->ApcStateIndex
!= OriginalApcEnvironment
)
568 /* We're already attached, so save the APC State into what we got */
569 KiAttachProcess(Thread
, Process
, OldIrql
, ApcState
);
573 /* We're not attached, so save the APC State into SavedApcState */
574 KiAttachProcess(Thread
, Process
, OldIrql
, &Thread
->SavedApcState
);
575 ApcState
->Process
= NULL
;
585 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState
)
589 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
591 /* Get the current thread and acquire the dispatcher lock */
592 Thread
= KeGetCurrentThread();
593 OldIrql
= KeAcquireDispatcherDatabaseLock();
595 /* Check for magic value meaning we were already in the same process */
596 if (ApcState
->Process
!= (PKPROCESS
)1)
599 * Check if the process isn't attacked, or has a Kernel APC in progress
600 * or has pending APC of any kind.
602 if ((Thread
->ApcStateIndex
== OriginalApcEnvironment
) ||
603 (Thread
->ApcState
.KernelApcInProgress
) ||
604 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) ||
605 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])))
607 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT
);
610 /* Decrease Stack Count */
611 if(!(--Thread
->ApcState
.Process
->StackCount
))
613 /* FIXME: Swap the process out */
616 if (ApcState
->Process
!= NULL
)
618 /* Restore the APC State */
619 KiMoveApcState(ApcState
, &Thread
->ApcState
);
623 /* The ApcState parameter is useless, so use the saved data and reset it */
624 KiMoveApcState(&Thread
->SavedApcState
, &Thread
->ApcState
);
625 Thread
->SavedApcState
.Process
= NULL
;
626 Thread
->ApcStateIndex
= OriginalApcEnvironment
;
627 Thread
->ApcStatePointer
[OriginalApcEnvironment
] = &Thread
->ApcState
;
628 Thread
->ApcStatePointer
[AttachedApcEnvironment
] = &Thread
->SavedApcState
;
631 /* Check if we have pending APCs */
632 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
634 /* What do you know, we do! Request them to be delivered */
635 Thread
->ApcState
.KernelApcPending
= TRUE
;
636 HalRequestSoftwareInterrupt(APC_LEVEL
);
640 KiSwapProcess(Thread
->ApcState
.Process
, Thread
->ApcState
.Process
);
643 /* Return to old IRQL*/
644 KeReleaseDispatcherDatabaseLock(OldIrql
);
652 KeAddSystemServiceTable(PULONG_PTR Base
,
653 PULONG Count OPTIONAL
,
658 /* Check if descriptor table entry is free */
659 if ((Index
> SSDT_MAX_ENTRIES
- 1) ||
660 (KeServiceDescriptorTable
[Index
].Base
) ||
661 (KeServiceDescriptorTableShadow
[Index
].Base
))
666 /* Initialize the shadow service descriptor table */
667 KeServiceDescriptorTableShadow
[Index
].Base
= Base
;
668 KeServiceDescriptorTableShadow
[Index
].Limit
= Limit
;
669 KeServiceDescriptorTableShadow
[Index
].Number
= Number
;
670 KeServiceDescriptorTableShadow
[Index
].Count
= Count
;
680 KeRemoveSystemServiceTable(IN ULONG Index
)
682 /* Make sure the Index is valid */
683 if (Index
> SSDT_MAX_ENTRIES
- 1) return FALSE
;
685 /* Is there a Normal Descriptor Table? */
686 if (!KeServiceDescriptorTable
[Index
].Base
)
688 /* Not with the index, is there a shadow at least? */
689 if (!KeServiceDescriptorTableShadow
[Index
].Base
) return FALSE
;
692 /* Now clear from the Shadow Table. */
693 KeServiceDescriptorTableShadow
[Index
].Base
= NULL
;
694 KeServiceDescriptorTableShadow
[Index
].Number
= NULL
;
695 KeServiceDescriptorTableShadow
[Index
].Limit
= 0;
696 KeServiceDescriptorTableShadow
[Index
].Count
= NULL
;
698 /* Check if we should clean from the Master one too */
701 KeServiceDescriptorTable
[Index
].Base
= NULL
;
702 KeServiceDescriptorTable
[Index
].Number
= NULL
;
703 KeServiceDescriptorTable
[Index
].Limit
= 0;
704 KeServiceDescriptorTable
[Index
].Count
= NULL
;