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