xref: /reactos/ntoskrnl/ke/procobj.c (revision 845faec4)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/procobj.c
5  * PURPOSE:         Kernel Process Management and System Call Tables
6  * PROGRAMMERS:     Alex Ionescu
7  *                  Gregor Anich
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 LIST_ENTRY KiProcessListHead;
19 LIST_ENTRY KiProcessInSwapListHead, KiProcessOutSwapListHead;
20 LIST_ENTRY KiStackInSwapListHead;
21 KEVENT KiSwapEvent;
22 
23 KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable[SSDT_MAX_ENTRIES];
24 KSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTableShadow[SSDT_MAX_ENTRIES];
25 
26 PVOID KeUserApcDispatcher;
27 PVOID KeUserCallbackDispatcher;
28 PVOID KeUserExceptionDispatcher;
29 PVOID KeRaiseUserExceptionDispatcher;
30 
31 /* PRIVATE FUNCTIONS *********************************************************/
32 
33 VOID
34 NTAPI
35 KiAttachProcess(IN PKTHREAD Thread,
36                 IN PKPROCESS Process,
37                 IN PKLOCK_QUEUE_HANDLE ApcLock,
38                 IN PRKAPC_STATE SavedApcState)
39 {
40 #if 0
41     PLIST_ENTRY ListHead, NextEntry;
42     PKTHREAD CurrentThread;
43 #endif
44     ASSERT(Process != Thread->ApcState.Process);
45 
46     /* Increase Stack Count */
47     ASSERT(Process->StackCount != MAXULONG_PTR);
48     Process->StackCount++;
49 
50     /* Swap the APC Environment */
51     KiMoveApcState(&Thread->ApcState, SavedApcState);
52 
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;
60 
61     /* Update Environment Pointers if needed*/
62     if (SavedApcState == &Thread->SavedApcState)
63     {
64         Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->
65                                                           SavedApcState;
66         Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
67         Thread->ApcStateIndex = AttachedApcEnvironment;
68     }
69 
70     /* Check if the process is paged in */
71     if (Process->State == ProcessInMemory)
72     {
73         /* Scan the ready list */
74 #if 0
75         ListHead = &Process->ReadyListHead;
76         NextEntry = ListHead->Flink;
77         while (NextEntry != ListHead)
78         {
79             /* Get the thread */
80             CurrentThread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
81 
82             /* Remove it */
83             RemoveEntryList(NextEntry);
84             CurrentThread->ProcessReadyQueue = FALSE;
85 
86             /* Mark it ready */
87             KiReadyThread(CurrentThread);
88 
89             /* Go to the next one */
90             NextEntry = ListHead->Flink;
91         }
92 #endif
93 
94         /* Release dispatcher lock */
95         KiReleaseDispatcherLockFromDpcLevel();
96 
97         /* Release lock */
98         KiReleaseApcLockFromDpcLevel(ApcLock);
99 
100         /* Swap Processes */
101         KiSwapProcess(Process, SavedApcState->Process);
102 
103         /* Exit the dispatcher */
104         KiExitDispatcher(ApcLock->OldIrql);
105     }
106     else
107     {
108         DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
109         ASSERT(FALSE);
110     }
111 }
112 
113 VOID
114 NTAPI
115 KeInitializeProcess(IN OUT PKPROCESS Process,
116                     IN KPRIORITY Priority,
117                     IN KAFFINITY Affinity,
118                     IN PULONG_PTR DirectoryTableBase,
119                     IN BOOLEAN Enable)
120 {
121 #ifdef CONFIG_SMP
122     ULONG i = 0;
123     UCHAR IdealNode = 0;
124     PKNODE Node;
125 #endif
126 
127     /* Initialize the Dispatcher Header */
128     Process->Header.Type = ProcessObject;
129     Process->Header.Size = sizeof(KPROCESS) / sizeof(ULONG);
130     Process->Header.SignalState = 0;
131     InitializeListHead(&(Process->Header.WaitListHead));
132 
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;
140 #if defined(_M_IX86)
141     Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE);
142 #endif
143 
144     /* Initialize the lists */
145     InitializeListHead(&Process->ThreadListHead);
146     InitializeListHead(&Process->ProfileListHead);
147     InitializeListHead(&Process->ReadyListHead);
148 
149     /* Initialize the current State */
150     Process->State = ProcessInMemory;
151 
152     /* Check how many Nodes there are on the system */
153 #ifdef CONFIG_SMP
154     if (KeNumberNodes > 1)
155     {
156         /* Set the new seed */
157         KeProcessNodeSeed = (KeProcessNodeSeed + 1) / KeNumberNodes;
158         IdealNode = KeProcessNodeSeed;
159 
160         /* Loop every node */
161         do
162         {
163             /* Check if the affinity matches */
164             if (KeNodeBlock[IdealNode]->ProcessorMask != Affinity) break;
165 
166             /* No match, try next Ideal Node and increase node loop index */
167             IdealNode++;
168             i++;
169 
170             /* Check if the Ideal Node is beyond the total number of nodes */
171             if (IdealNode >= KeNumberNodes)
172             {
173                 /* Normalize the Ideal Node */
174                 IdealNode -= KeNumberNodes;
175             }
176         } while (i < KeNumberNodes);
177     }
178 
179     /* Set the ideal node and get the ideal node block */
180     Process->IdealNode = IdealNode;
181     Node = KeNodeBlock[IdealNode];
182     ASSERT(Node->ProcessorMask & Affinity);
183 
184     /* Find the matching affinity set to calculate the thread seed */
185     Affinity &= Node->ProcessorMask;
186     Process->ThreadSeed = KeFindNextRightSetAffinity(Node->Seed,
187                                                      (ULONG)Affinity);
188     Node->Seed = Process->ThreadSeed;
189 #endif
190 }
191 
192 ULONG
193 NTAPI
194 KeSetProcess(IN PKPROCESS Process,
195              IN KPRIORITY Increment,
196              IN BOOLEAN InWait)
197 {
198     KIRQL OldIrql;
199     ULONG OldState;
200     ASSERT_PROCESS(Process);
201     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
202 
203     /* Lock Dispatcher */
204     OldIrql = KiAcquireDispatcherLock();
205 
206     /* Get Old State */
207     OldState = Process->Header.SignalState;
208 
209     /* Signal the Process */
210     Process->Header.SignalState = TRUE;
211 
212     /* Check if was unsignaled and has waiters */
213     if (!(OldState) &&
214         !(IsListEmpty(&Process->Header.WaitListHead)))
215     {
216         /* Unwait the threads */
217         KxUnwaitThread(&Process->Header, Increment);
218     }
219 
220     /* Release Dispatcher Database */
221     KiReleaseDispatcherLock(OldIrql);
222 
223     /* Return the previous State */
224     return OldState;
225 }
226 
227 VOID
228 NTAPI
229 KeSetQuantumProcess(IN PKPROCESS Process,
230                     IN UCHAR Quantum)
231 {
232     KLOCK_QUEUE_HANDLE ProcessLock;
233     PLIST_ENTRY NextEntry, ListHead;
234     PKTHREAD Thread;
235     ASSERT_PROCESS(Process);
236     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
237 
238     /* Lock the process */
239     KiAcquireProcessLock(Process, &ProcessLock);
240 
241     /* Set new quantum */
242     Process->QuantumReset = Quantum;
243 
244     /* Loop all child threads */
245     ListHead = &Process->ThreadListHead;
246     NextEntry = ListHead->Flink;
247     while (ListHead != NextEntry)
248     {
249         /* Get the thread */
250         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
251 
252         /* Set quantum */
253         Thread->QuantumReset = Quantum;
254 
255         /* Go to the next one */
256         NextEntry = NextEntry->Flink;
257     }
258 
259     /* Release lock */
260     KiReleaseProcessLock(&ProcessLock);
261 }
262 
263 KAFFINITY
264 NTAPI
265 KeSetAffinityProcess(IN PKPROCESS Process,
266                      IN KAFFINITY Affinity)
267 {
268 
269     KLOCK_QUEUE_HANDLE ProcessLock;
270     PLIST_ENTRY NextEntry, ListHead;
271     KAFFINITY OldAffinity;
272     PKTHREAD Thread;
273     ASSERT_PROCESS(Process);
274     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
275     ASSERT((Affinity & KeActiveProcessors) != 0);
276 
277     /* Lock the process */
278     KiAcquireProcessLock(Process, &ProcessLock);
279 
280     /* Acquire the dispatcher lock */
281     KiAcquireDispatcherLockAtDpcLevel();
282 
283     /* Capture old affinity and update it */
284     OldAffinity = Process->Affinity;
285     Process->Affinity = Affinity;
286 
287     /* Loop all child threads */
288     ListHead = &Process->ThreadListHead;
289     NextEntry = ListHead->Flink;
290     while (ListHead != NextEntry)
291     {
292         /* Get the thread */
293         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
294 
295         /* Set affinity on it */
296         KiSetAffinityThread(Thread, Affinity);
297         NextEntry = NextEntry->Flink;
298     }
299 
300     /* Release Dispatcher Database */
301     KiReleaseDispatcherLockFromDpcLevel();
302 
303     /* Release the process lock */
304     KiReleaseProcessLockFromDpcLevel(&ProcessLock);
305     KiExitDispatcher(ProcessLock.OldIrql);
306 
307     /* Return previous affinity */
308     return OldAffinity;
309 }
310 
311 BOOLEAN
312 NTAPI
313 KeSetAutoAlignmentProcess(IN PKPROCESS Process,
314                           IN BOOLEAN Enable)
315 {
316     /* Set or reset the bit depending on what the enable flag says */
317     if (Enable)
318     {
319         return InterlockedBitTestAndSet(&Process->ProcessFlags,
320                                         KPSF_AUTO_ALIGNMENT_BIT);
321     }
322     else
323     {
324         return InterlockedBitTestAndReset(&Process->ProcessFlags,
325                                           KPSF_AUTO_ALIGNMENT_BIT);
326     }
327 }
328 
329 BOOLEAN
330 NTAPI
331 KeSetDisableBoostProcess(IN PKPROCESS Process,
332                          IN BOOLEAN Disable)
333 {
334     /* Set or reset the bit depending on what the disable flag says */
335     if (Disable)
336     {
337         return InterlockedBitTestAndSet(&Process->ProcessFlags,
338                                         KPSF_DISABLE_BOOST_BIT);
339     }
340     else
341     {
342         return InterlockedBitTestAndReset(&Process->ProcessFlags,
343                                           KPSF_DISABLE_BOOST_BIT);
344     }
345 }
346 
347 KPRIORITY
348 NTAPI
349 KeSetPriorityAndQuantumProcess(IN PKPROCESS Process,
350                                IN KPRIORITY Priority,
351                                IN UCHAR Quantum OPTIONAL)
352 {
353     KLOCK_QUEUE_HANDLE ProcessLock;
354     KPRIORITY Delta;
355     PLIST_ENTRY NextEntry, ListHead;
356     KPRIORITY NewPriority, OldPriority;
357     PKTHREAD Thread;
358     ASSERT_PROCESS(Process);
359     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
360 
361     /* Check if the process already has this priority */
362     if (Process->BasePriority == Priority) return Process->BasePriority;
363 
364     /* If the caller gave priority 0, normalize to 1 */
365     if (!Priority) Priority = LOW_PRIORITY + 1;
366 
367     /* Lock the process */
368     KiAcquireProcessLock(Process, &ProcessLock);
369 
370     /* Check if we are modifying the quantum too */
371     if (Quantum) Process->QuantumReset = Quantum;
372 
373     /* Save the current base priority and update it */
374     OldPriority = Process->BasePriority;
375     Process->BasePriority = (SCHAR)Priority;
376 
377     /* Calculate the priority delta */
378     Delta = Priority - OldPriority;
379 
380     /* Set the list head and list entry */
381     ListHead = &Process->ThreadListHead;
382     NextEntry = ListHead->Flink;
383 
384     /* Check if this is a real-time priority */
385     if (Priority >= LOW_REALTIME_PRIORITY)
386     {
387         /* Loop the thread list */
388         while (NextEntry != ListHead)
389         {
390             /* Get the thread */
391             Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
392 
393             /* Update the quantum if we had one */
394             if (Quantum) Thread->QuantumReset = Quantum;
395 
396             /* Acquire the thread lock */
397             KiAcquireThreadLock(Thread);
398 
399             /* Calculate the new priority */
400             NewPriority = Thread->BasePriority + Delta;
401             if (NewPriority < LOW_REALTIME_PRIORITY)
402             {
403                 /* We're in real-time range, don't let it go below */
404                 NewPriority = LOW_REALTIME_PRIORITY;
405             }
406             else if (NewPriority > HIGH_PRIORITY)
407             {
408                 /* We're going beyond the maximum priority, normalize */
409                 NewPriority = HIGH_PRIORITY;
410             }
411 
412             /*
413              * If priority saturation occured or the old priority was still in
414              * the real-time range, don't do anything.
415              */
416             if (!(Thread->Saturation) || (OldPriority < LOW_REALTIME_PRIORITY))
417             {
418                 /* Check if we had priority saturation */
419                 if (Thread->Saturation > 0)
420                 {
421                     /* Boost priority to maximum */
422                     NewPriority = HIGH_PRIORITY;
423                 }
424                 else if (Thread->Saturation < 0)
425                 {
426                     /* If we had negative saturation, set minimum priority */
427                     NewPriority = LOW_REALTIME_PRIORITY;
428                 }
429 
430                 /* Update priority and quantum */
431                 Thread->BasePriority = (SCHAR)NewPriority;
432                 Thread->Quantum = Thread->QuantumReset;
433 
434                 /* Disable decrements and update priority */
435                 Thread->PriorityDecrement = 0;
436                 KiSetPriorityThread(Thread, NewPriority);
437             }
438 
439             /* Release the thread lock */
440             KiReleaseThreadLock(Thread);
441 
442             /* Go to the next thread */
443             NextEntry = NextEntry->Flink;
444         }
445     }
446     else
447     {
448         /* Loop the thread list */
449         while (NextEntry != ListHead)
450         {
451             /* Get the thread */
452             Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
453 
454             /* Update the quantum if we had one */
455             if (Quantum) Thread->QuantumReset = Quantum;
456 
457             /* Lock the thread */
458             KiAcquireThreadLock(Thread);
459 
460             /* Calculate the new priority */
461             NewPriority = Thread->BasePriority + Delta;
462             if (NewPriority >= LOW_REALTIME_PRIORITY)
463             {
464                 /* We're not real-time range, don't let it enter RT range */
465                 NewPriority = LOW_REALTIME_PRIORITY - 1;
466             }
467             else if (NewPriority <= LOW_PRIORITY)
468             {
469                 /* We're going below the minimum priority, normalize */
470                 NewPriority = 1;
471             }
472 
473             /*
474              * If priority saturation occured or the old priority was still in
475              * the real-time range, don't do anything.
476              */
477             if (!(Thread->Saturation) ||
478                 (OldPriority >= LOW_REALTIME_PRIORITY))
479             {
480                 /* Check if we had priority saturation */
481                 if (Thread->Saturation > 0)
482                 {
483                     /* Boost priority to maximum */
484                     NewPriority = LOW_REALTIME_PRIORITY - 1;
485                 }
486                 else if (Thread->Saturation < 0)
487                 {
488                     /* If we had negative saturation, set minimum priority */
489                     NewPriority = 1;
490                 }
491 
492                 /* Update priority and quantum */
493                 Thread->BasePriority = (SCHAR)NewPriority;
494                 Thread->Quantum = Thread->QuantumReset;
495 
496                 /* Disable decrements and update priority */
497                 Thread->PriorityDecrement = 0;
498                 KiSetPriorityThread(Thread, NewPriority);
499             }
500 
501             /* Release the thread lock */
502             KiReleaseThreadLock(Thread);
503 
504             /* Go to the next thread */
505             NextEntry = NextEntry->Flink;
506         }
507     }
508 
509     /* Release Dispatcher Database */
510     KiReleaseDispatcherLockFromDpcLevel();
511 
512     /* Release the process lock */
513     KiReleaseProcessLockFromDpcLevel(&ProcessLock);
514     KiExitDispatcher(ProcessLock.OldIrql);
515 
516     /* Return previous priority */
517     return OldPriority;
518 }
519 
520 /* PUBLIC FUNCTIONS **********************************************************/
521 
522 /*
523  * @implemented
524  */
525 VOID
526 NTAPI
527 KeAttachProcess(IN PKPROCESS Process)
528 {
529     KLOCK_QUEUE_HANDLE ApcLock;
530     PKTHREAD Thread = KeGetCurrentThread();
531     ASSERT_PROCESS(Process);
532     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
533 
534     /* Check if we're already in that process */
535     if (Thread->ApcState.Process == Process) return;
536 
537     /* Check if a DPC is executing or if we're already attached */
538     if ((Thread->ApcStateIndex != OriginalApcEnvironment) ||
539         (KeIsExecutingDpc()))
540     {
541         /* Invalid attempt */
542         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
543                      (ULONG_PTR)Process,
544                      (ULONG_PTR)Thread->ApcState.Process,
545                      Thread->ApcStateIndex,
546                      KeIsExecutingDpc());
547     }
548     else
549     {
550         /* Acquire APC Lock */
551         KiAcquireApcLock(Thread, &ApcLock);
552 
553         /* Acquire the dispatcher lock */
554         KiAcquireDispatcherLockAtDpcLevel();
555 
556         /* Legit attach attempt: do it! */
557         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
558     }
559 }
560 
561 /*
562  * @implemented
563  */
564 VOID
565 NTAPI
566 KeDetachProcess(VOID)
567 {
568     PKTHREAD Thread = KeGetCurrentThread();
569     KLOCK_QUEUE_HANDLE ApcLock;
570     PKPROCESS Process;
571     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
572 
573     /* Check if it's attached */
574     if (Thread->ApcStateIndex == OriginalApcEnvironment) return;
575 
576     /* Acquire APC Lock */
577     KiAcquireApcLock(Thread, &ApcLock);
578 
579     /* Check for invalid attach attempts */
580     if ((Thread->ApcState.KernelApcInProgress) ||
581         !(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
582         !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
583     {
584         /* Crash the system */
585         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
586     }
587 
588     /* Get the process */
589     Process = Thread->ApcState.Process;
590 
591     /* Acquire dispatcher lock */
592     KiAcquireDispatcherLockAtDpcLevel();
593 
594     /* Decrease the stack count */
595     ASSERT(Process->StackCount != 0);
596     ASSERT(Process->State == ProcessInMemory);
597     Process->StackCount--;
598 
599     /* Check if we can swap the process out */
600     if (!Process->StackCount)
601     {
602         /* FIXME: Swap the process out */
603     }
604 
605     /* Release dispatcher lock */
606     KiReleaseDispatcherLockFromDpcLevel();
607 
608     /* Restore the APC State */
609     KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
610     Thread->SavedApcState.Process = NULL;
611     Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
612     Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
613     Thread->ApcStateIndex = OriginalApcEnvironment;
614 
615     /* Release lock */
616     KiReleaseApcLockFromDpcLevel(&ApcLock);
617 
618     /* Swap Processes */
619     KiSwapProcess(Thread->ApcState.Process, Process);
620 
621     /* Exit the dispatcher */
622     KiExitDispatcher(ApcLock.OldIrql);
623 
624     /* Check if we have pending APCs */
625     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
626     {
627         /* What do you know, we do! Request them to be delivered */
628         Thread->ApcState.KernelApcPending = TRUE;
629         HalRequestSoftwareInterrupt(APC_LEVEL);
630     }
631 }
632 
633 /*
634  * @implemented
635  */
636 BOOLEAN
637 NTAPI
638 KeIsAttachedProcess(VOID)
639 {
640     /* Return the APC State */
641     return KeGetCurrentThread()->ApcStateIndex;
642 }
643 
644 /*
645  * @implemented
646  */
647 VOID
648 NTAPI
649 KeStackAttachProcess(IN PKPROCESS Process,
650                      OUT PRKAPC_STATE ApcState)
651 {
652     KLOCK_QUEUE_HANDLE ApcLock;
653     PKTHREAD Thread = KeGetCurrentThread();
654     ASSERT_PROCESS(Process);
655     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
656 
657     /* Crash system if DPC is being executed! */
658     if (KeIsExecutingDpc())
659     {
660         /* Executing a DPC, crash! */
661         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
662                      (ULONG_PTR)Process,
663                      (ULONG_PTR)Thread->ApcState.Process,
664                      Thread->ApcStateIndex,
665                      KeIsExecutingDpc());
666     }
667 
668     /* Check if we are already in the target process */
669     if (Thread->ApcState.Process == Process)
670     {
671         /* Set magic value so we don't crash later when detaching */
672         ApcState->Process = (PKPROCESS)1;
673         return;
674     }
675 
676     /* Acquire APC Lock */
677     KiAcquireApcLock(Thread, &ApcLock);
678 
679     /* Acquire dispatcher lock */
680     KiAcquireDispatcherLockAtDpcLevel();
681 
682     /* Check if the Current Thread is already attached */
683     if (Thread->ApcStateIndex != OriginalApcEnvironment)
684     {
685         /* We're already attached, so save the APC State into what we got */
686         KiAttachProcess(Thread, Process, &ApcLock, ApcState);
687     }
688     else
689     {
690         /* We're not attached, so save the APC State into SavedApcState */
691         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
692         ApcState->Process = NULL;
693     }
694 }
695 
696 /*
697  * @implemented
698  */
699 VOID
700 NTAPI
701 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
702 {
703     KLOCK_QUEUE_HANDLE ApcLock;
704     PKTHREAD Thread = KeGetCurrentThread();
705     PKPROCESS Process;
706     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
707 
708     /* Check for magic value meaning we were already in the same process */
709     if (ApcState->Process == (PKPROCESS)1) return;
710 
711     /* Loop to make sure no APCs are pending  */
712     for (;;)
713     {
714         /* Acquire APC Lock */
715         KiAcquireApcLock(Thread, &ApcLock);
716 
717         /* Check if a kernel APC is pending */
718         if (Thread->ApcState.KernelApcPending)
719         {
720             /* Check if kernel APC should be delivered */
721             if (!(Thread->KernelApcDisable) && (ApcLock.OldIrql <= APC_LEVEL))
722             {
723                 /* Release the APC lock so that the APC can be delivered */
724                 KiReleaseApcLock(&ApcLock);
725                 continue;
726             }
727         }
728 
729         /* Otherwise, break out */
730         break;
731     }
732 
733     /*
734      * Check if the process isn't attacked, or has a Kernel APC in progress
735      * or has pending APC of any kind.
736      */
737     if ((Thread->ApcStateIndex == OriginalApcEnvironment) ||
738         (Thread->ApcState.KernelApcInProgress) ||
739         (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
740         (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
741     {
742         /* Bugcheck the system */
743         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
744     }
745 
746     /* Get the process */
747     Process = Thread->ApcState.Process;
748 
749     /* Acquire dispatcher lock */
750     KiAcquireDispatcherLockAtDpcLevel();
751 
752     /* Decrease the stack count */
753     ASSERT(Process->StackCount != 0);
754     ASSERT(Process->State == ProcessInMemory);
755     Process->StackCount--;
756 
757     /* Check if we can swap the process out */
758     if (!Process->StackCount)
759     {
760         /* FIXME: Swap the process out */
761     }
762 
763     /* Release dispatcher lock */
764     KiReleaseDispatcherLockFromDpcLevel();
765 
766     /* Check if there's an APC state to restore */
767     if (ApcState->Process)
768     {
769         /* Restore the APC State */
770         KiMoveApcState(ApcState, &Thread->ApcState);
771     }
772     else
773     {
774         /* The ApcState parameter is useless, so use the saved data and reset it */
775         KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
776         Thread->SavedApcState.Process = NULL;
777         Thread->ApcStateIndex = OriginalApcEnvironment;
778         Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
779         Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
780     }
781 
782     /* Release lock */
783     KiReleaseApcLockFromDpcLevel(&ApcLock);
784 
785     /* Swap Processes */
786     KiSwapProcess(Thread->ApcState.Process, Process);
787 
788     /* Exit the dispatcher */
789     KiExitDispatcher(ApcLock.OldIrql);
790 
791     /* Check if we have pending APCs */
792     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
793     {
794         /* What do you know, we do! Request them to be delivered */
795         Thread->ApcState.KernelApcPending = TRUE;
796         HalRequestSoftwareInterrupt(APC_LEVEL);
797     }
798 }
799 
800 /*
801  * @implemented
802  */
803 ULONG
804 NTAPI
805 KeQueryRuntimeProcess(IN PKPROCESS Process,
806                       OUT PULONG UserTime)
807 {
808     ULONG TotalUser, TotalKernel;
809     KLOCK_QUEUE_HANDLE ProcessLock;
810     PLIST_ENTRY NextEntry, ListHead;
811     PKTHREAD Thread;
812 
813     ASSERT_PROCESS(Process);
814 
815     /* Initialize user and kernel times */
816     TotalUser = Process->UserTime;
817     TotalKernel = Process->KernelTime;
818 
819     /* Lock the process */
820     KiAcquireProcessLock(Process, &ProcessLock);
821 
822     /* Loop all child threads and sum up their times */
823     ListHead = &Process->ThreadListHead;
824     NextEntry = ListHead->Flink;
825     while (ListHead != NextEntry)
826     {
827         /* Get the thread */
828         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
829 
830         /* Sum up times */
831         TotalKernel += Thread->KernelTime;
832         TotalUser += Thread->UserTime;
833 
834         /* Go to the next one */
835         NextEntry = NextEntry->Flink;
836     }
837 
838     /* Release lock */
839     KiReleaseProcessLock(&ProcessLock);
840 
841     /* Return the user time */
842     *UserTime = TotalUser;
843 
844     /* Return the kernel time */
845     return TotalKernel;
846 }
847 
848 /*
849  * @implemented
850  */
851 BOOLEAN
852 NTAPI
853 KeAddSystemServiceTable(IN PULONG_PTR Base,
854                         IN PULONG Count OPTIONAL,
855                         IN ULONG Limit,
856                         IN PUCHAR Number,
857                         IN ULONG Index)
858 {
859     PAGED_CODE();
860 
861     /* Check if descriptor table entry is free */
862     if ((Index > SSDT_MAX_ENTRIES - 1) ||
863         (KeServiceDescriptorTable[Index].Base) ||
864         (KeServiceDescriptorTableShadow[Index].Base))
865     {
866         /* It's not, fail */
867         return FALSE;
868     }
869 
870     /* Initialize the shadow service descriptor table */
871     KeServiceDescriptorTableShadow[Index].Base = Base;
872     KeServiceDescriptorTableShadow[Index].Limit = Limit;
873     KeServiceDescriptorTableShadow[Index].Number = Number;
874     KeServiceDescriptorTableShadow[Index].Count = Count;
875     return TRUE;
876 }
877 
878 /*
879  * @implemented
880  */
881 BOOLEAN
882 NTAPI
883 KeRemoveSystemServiceTable(IN ULONG Index)
884 {
885     PAGED_CODE();
886 
887     /* Make sure the Index is valid */
888     if (Index > (SSDT_MAX_ENTRIES - 1)) return FALSE;
889 
890     /* Is there a Normal Descriptor Table? */
891     if (!KeServiceDescriptorTable[Index].Base)
892     {
893         /* Not with the index, is there a shadow at least? */
894         if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
895     }
896 
897     /* Now clear from the Shadow Table. */
898     KeServiceDescriptorTableShadow[Index].Base = NULL;
899     KeServiceDescriptorTableShadow[Index].Number = NULL;
900     KeServiceDescriptorTableShadow[Index].Limit = 0;
901     KeServiceDescriptorTableShadow[Index].Count = NULL;
902 
903     /* Check if we should clean from the Master one too */
904     if (Index == 1)
905     {
906         KeServiceDescriptorTable[Index].Base = NULL;
907         KeServiceDescriptorTable[Index].Number = NULL;
908         KeServiceDescriptorTable[Index].Limit = 0;
909         KeServiceDescriptorTable[Index].Count = NULL;
910     }
911 
912     /* Return success */
913     return TRUE;
914 }
915 /* EOF */
916