xref: /reactos/ntoskrnl/ke/procobj.c (revision 9393fc32)
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         KiReleaseDispatcherLockFromSynchLevel();
96 
97         /* Release lock */
98         KiReleaseApcLockFromSynchLevel(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     KiAcquireProcessLockRaiseToSynch(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     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
279 
280     /* Acquire the dispatcher lock */
281     KiAcquireDispatcherLockAtSynchLevel();
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     KiReleaseDispatcherLockFromSynchLevel();
302 
303     /* Release the process lock */
304     KiReleaseProcessLockFromSynchLevel(&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     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
369 
370     /* Acquire the dispatcher lock */
371     KiAcquireDispatcherLockAtSynchLevel();
372 
373     /* Check if we are modifying the quantum too */
374     if (Quantum) Process->QuantumReset = Quantum;
375 
376     /* Save the current base priority and update it */
377     OldPriority = Process->BasePriority;
378     Process->BasePriority = (SCHAR)Priority;
379 
380     /* Calculate the priority delta */
381     Delta = Priority - OldPriority;
382 
383     /* Set the list head and list entry */
384     ListHead = &Process->ThreadListHead;
385     NextEntry = ListHead->Flink;
386 
387     /* Check if this is a real-time priority */
388     if (Priority >= LOW_REALTIME_PRIORITY)
389     {
390         /* Loop the thread list */
391         while (NextEntry != ListHead)
392         {
393             /* Get the thread */
394             Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
395 
396             /* Update the quantum if we had one */
397             if (Quantum) Thread->QuantumReset = Quantum;
398 
399             /* Acquire the thread lock */
400             KiAcquireThreadLock(Thread);
401 
402             /* Calculate the new priority */
403             NewPriority = Thread->BasePriority + Delta;
404             if (NewPriority < LOW_REALTIME_PRIORITY)
405             {
406                 /* We're in real-time range, don't let it go below */
407                 NewPriority = LOW_REALTIME_PRIORITY;
408             }
409             else if (NewPriority > HIGH_PRIORITY)
410             {
411                 /* We're going beyond the maximum priority, normalize */
412                 NewPriority = HIGH_PRIORITY;
413             }
414 
415             /*
416              * If priority saturation occured or the old priority was still in
417              * the real-time range, don't do anything.
418              */
419             if (!(Thread->Saturation) || (OldPriority < LOW_REALTIME_PRIORITY))
420             {
421                 /* Check if we had priority saturation */
422                 if (Thread->Saturation > 0)
423                 {
424                     /* Boost priority to maximum */
425                     NewPriority = HIGH_PRIORITY;
426                 }
427                 else if (Thread->Saturation < 0)
428                 {
429                     /* If we had negative saturation, set minimum priority */
430                     NewPriority = LOW_REALTIME_PRIORITY;
431                 }
432 
433                 /* Update priority and quantum */
434                 Thread->BasePriority = (SCHAR)NewPriority;
435                 Thread->Quantum = Thread->QuantumReset;
436 
437                 /* Disable decrements and update priority */
438                 Thread->PriorityDecrement = 0;
439                 KiSetPriorityThread(Thread, NewPriority);
440             }
441 
442             /* Release the thread lock */
443             KiReleaseThreadLock(Thread);
444 
445             /* Go to the next thread */
446             NextEntry = NextEntry->Flink;
447         }
448     }
449     else
450     {
451         /* Loop the thread list */
452         while (NextEntry != ListHead)
453         {
454             /* Get the thread */
455             Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
456 
457             /* Update the quantum if we had one */
458             if (Quantum) Thread->QuantumReset = Quantum;
459 
460             /* Lock the thread */
461             KiAcquireThreadLock(Thread);
462 
463             /* Calculate the new priority */
464             NewPriority = Thread->BasePriority + Delta;
465             if (NewPriority >= LOW_REALTIME_PRIORITY)
466             {
467                 /* We're not real-time range, don't let it enter RT range */
468                 NewPriority = LOW_REALTIME_PRIORITY - 1;
469             }
470             else if (NewPriority <= LOW_PRIORITY)
471             {
472                 /* We're going below the minimum priority, normalize */
473                 NewPriority = 1;
474             }
475 
476             /*
477              * If priority saturation occured or the old priority was still in
478              * the real-time range, don't do anything.
479              */
480             if (!(Thread->Saturation) ||
481                 (OldPriority >= LOW_REALTIME_PRIORITY))
482             {
483                 /* Check if we had priority saturation */
484                 if (Thread->Saturation > 0)
485                 {
486                     /* Boost priority to maximum */
487                     NewPriority = LOW_REALTIME_PRIORITY - 1;
488                 }
489                 else if (Thread->Saturation < 0)
490                 {
491                     /* If we had negative saturation, set minimum priority */
492                     NewPriority = 1;
493                 }
494 
495                 /* Update priority and quantum */
496                 Thread->BasePriority = (SCHAR)NewPriority;
497                 Thread->Quantum = Thread->QuantumReset;
498 
499                 /* Disable decrements and update priority */
500                 Thread->PriorityDecrement = 0;
501                 KiSetPriorityThread(Thread, NewPriority);
502             }
503 
504             /* Release the thread lock */
505             KiReleaseThreadLock(Thread);
506 
507             /* Go to the next thread */
508             NextEntry = NextEntry->Flink;
509         }
510     }
511 
512     /* Release Dispatcher Database */
513     KiReleaseDispatcherLockFromSynchLevel();
514 
515     /* Release the process lock */
516     KiReleaseProcessLockFromSynchLevel(&ProcessLock);
517     KiExitDispatcher(ProcessLock.OldIrql);
518 
519     /* Return previous priority */
520     return OldPriority;
521 }
522 
523 VOID
524 NTAPI
525 KeQueryValuesProcess(IN PKPROCESS Process,
526                      PPROCESS_VALUES Values)
527 {
528     PEPROCESS EProcess;
529     PLIST_ENTRY NextEntry;
530     ULONG TotalKernel, TotalUser;
531     KLOCK_QUEUE_HANDLE ProcessLock;
532 
533     ASSERT_PROCESS(Process);
534     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
535 
536     /* Lock the process */
537     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
538 
539     /* Initialize user and kernel times */
540     TotalKernel = Process->KernelTime;
541     TotalUser = Process->UserTime;
542 
543     /* Copy the IO_COUNTERS from the process */
544     EProcess = (PEPROCESS)Process;
545     Values->IoInfo.ReadOperationCount = EProcess->ReadOperationCount.QuadPart;
546     Values->IoInfo.WriteOperationCount = EProcess->WriteOperationCount.QuadPart;
547     Values->IoInfo.OtherOperationCount = EProcess->OtherOperationCount.QuadPart;
548     Values->IoInfo.ReadTransferCount = EProcess->ReadTransferCount.QuadPart;
549     Values->IoInfo.WriteTransferCount = EProcess->WriteTransferCount.QuadPart;
550     Values->IoInfo.OtherTransferCount = EProcess->OtherTransferCount.QuadPart;
551 
552     /* Loop all child threads and sum up their times */
553     for (NextEntry = Process->ThreadListHead.Flink;
554          NextEntry != &Process->ThreadListHead;
555          NextEntry = NextEntry->Flink)
556     {
557         PKTHREAD Thread;
558 
559         /* Get the thread */
560         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
561 
562         /* Sum up times */
563         TotalKernel += Thread->KernelTime;
564         TotalUser += Thread->UserTime;
565     }
566 
567     /* Release the process lock */
568     KiReleaseProcessLock(&ProcessLock);
569 
570     /* Compute total times */
571     Values->TotalKernelTime.QuadPart = TotalKernel * (LONGLONG)KeMaximumIncrement;
572     Values->TotalUserTime.QuadPart = TotalUser * (LONGLONG)KeMaximumIncrement;
573 }
574 
575 /* PUBLIC FUNCTIONS **********************************************************/
576 
577 /*
578  * @implemented
579  */
580 VOID
581 NTAPI
582 KeAttachProcess(IN PKPROCESS Process)
583 {
584     KLOCK_QUEUE_HANDLE ApcLock;
585     PKTHREAD Thread = KeGetCurrentThread();
586     ASSERT_PROCESS(Process);
587     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
588 
589     /* Check if we're already in that process */
590     if (Thread->ApcState.Process == Process) return;
591 
592     /* Check if a DPC is executing or if we're already attached */
593     if ((Thread->ApcStateIndex != OriginalApcEnvironment) ||
594         (KeIsExecutingDpc()))
595     {
596         /* Invalid attempt */
597         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
598                      (ULONG_PTR)Process,
599                      (ULONG_PTR)Thread->ApcState.Process,
600                      Thread->ApcStateIndex,
601                      KeIsExecutingDpc());
602     }
603     else
604     {
605         /* Acquire APC Lock */
606         KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
607 
608         /* Acquire the dispatcher lock */
609         KiAcquireDispatcherLockAtSynchLevel();
610 
611         /* Legit attach attempt: do it! */
612         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
613     }
614 }
615 
616 /*
617  * @implemented
618  */
619 VOID
620 NTAPI
621 KeDetachProcess(VOID)
622 {
623     PKTHREAD Thread = KeGetCurrentThread();
624     KLOCK_QUEUE_HANDLE ApcLock;
625     PKPROCESS Process;
626     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
627 
628     /* Check if it's attached */
629     if (Thread->ApcStateIndex == OriginalApcEnvironment) return;
630 
631     /* Acquire APC Lock */
632     KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
633 
634     /* Check for invalid attach attempts */
635     if ((Thread->ApcState.KernelApcInProgress) ||
636         !(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
637         !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
638     {
639         /* Crash the system */
640         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
641     }
642 
643     /* Get the process */
644     Process = Thread->ApcState.Process;
645 
646     /* Acquire dispatcher lock */
647     KiAcquireDispatcherLockAtSynchLevel();
648 
649     /* Decrease the stack count */
650     ASSERT(Process->StackCount != 0);
651     ASSERT(Process->State == ProcessInMemory);
652     Process->StackCount--;
653 
654     /* Check if we can swap the process out */
655     if (!Process->StackCount)
656     {
657         /* FIXME: Swap the process out */
658     }
659 
660     /* Release dispatcher lock */
661     KiReleaseDispatcherLockFromSynchLevel();
662 
663     /* Restore the APC State */
664     KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
665     Thread->SavedApcState.Process = NULL;
666     Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
667     Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
668     Thread->ApcStateIndex = OriginalApcEnvironment;
669 
670     /* Release lock */
671     KiReleaseApcLockFromSynchLevel(&ApcLock);
672 
673     /* Swap Processes */
674     KiSwapProcess(Thread->ApcState.Process, Process);
675 
676     /* Exit the dispatcher */
677     KiExitDispatcher(ApcLock.OldIrql);
678 
679     /* Check if we have pending APCs */
680     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
681     {
682         /* What do you know, we do! Request them to be delivered */
683         Thread->ApcState.KernelApcPending = TRUE;
684         HalRequestSoftwareInterrupt(APC_LEVEL);
685     }
686 }
687 
688 /*
689  * @implemented
690  */
691 BOOLEAN
692 NTAPI
693 KeIsAttachedProcess(VOID)
694 {
695     /* Return the APC State */
696     return KeGetCurrentThread()->ApcStateIndex;
697 }
698 
699 /*
700  * @implemented
701  */
702 VOID
703 NTAPI
704 KeStackAttachProcess(IN PKPROCESS Process,
705                      OUT PRKAPC_STATE ApcState)
706 {
707     KLOCK_QUEUE_HANDLE ApcLock;
708     PKTHREAD Thread = KeGetCurrentThread();
709     ASSERT_PROCESS(Process);
710     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
711 
712     /* Crash system if DPC is being executed! */
713     if (KeIsExecutingDpc())
714     {
715         /* Executing a DPC, crash! */
716         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
717                      (ULONG_PTR)Process,
718                      (ULONG_PTR)Thread->ApcState.Process,
719                      Thread->ApcStateIndex,
720                      KeIsExecutingDpc());
721     }
722 
723     /* Check if we are already in the target process */
724     if (Thread->ApcState.Process == Process)
725     {
726         /* Set magic value so we don't crash later when detaching */
727         ApcState->Process = (PKPROCESS)1;
728         return;
729     }
730 
731     /* Acquire APC Lock */
732     KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
733 
734     /* Acquire dispatcher lock */
735     KiAcquireDispatcherLockAtSynchLevel();
736 
737     /* Check if the Current Thread is already attached */
738     if (Thread->ApcStateIndex != OriginalApcEnvironment)
739     {
740         /* We're already attached, so save the APC State into what we got */
741         KiAttachProcess(Thread, Process, &ApcLock, ApcState);
742     }
743     else
744     {
745         /* We're not attached, so save the APC State into SavedApcState */
746         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
747         ApcState->Process = NULL;
748     }
749 }
750 
751 /*
752  * @implemented
753  */
754 VOID
755 NTAPI
756 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
757 {
758     KLOCK_QUEUE_HANDLE ApcLock;
759     PKTHREAD Thread = KeGetCurrentThread();
760     PKPROCESS Process;
761     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
762 
763     /* Check for magic value meaning we were already in the same process */
764     if (ApcState->Process == (PKPROCESS)1) return;
765 
766     /* Loop to make sure no APCs are pending  */
767     for (;;)
768     {
769         /* Acquire APC Lock */
770         KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
771 
772         /* Check if a kernel APC is pending */
773         if (Thread->ApcState.KernelApcPending)
774         {
775             /* Check if kernel APC should be delivered */
776             if (!(Thread->KernelApcDisable) && (ApcLock.OldIrql <= APC_LEVEL))
777             {
778                 /* Release the APC lock so that the APC can be delivered */
779                 KiReleaseApcLock(&ApcLock);
780                 continue;
781             }
782         }
783 
784         /* Otherwise, break out */
785         break;
786     }
787 
788     /*
789      * Check if the process isn't attacked, or has a Kernel APC in progress
790      * or has pending APC of any kind.
791      */
792     if ((Thread->ApcStateIndex == OriginalApcEnvironment) ||
793         (Thread->ApcState.KernelApcInProgress) ||
794         (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
795         (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
796     {
797         /* Bugcheck the system */
798         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
799     }
800 
801     /* Get the process */
802     Process = Thread->ApcState.Process;
803 
804     /* Acquire dispatcher lock */
805     KiAcquireDispatcherLockAtSynchLevel();
806 
807     /* Decrease the stack count */
808     ASSERT(Process->StackCount != 0);
809     ASSERT(Process->State == ProcessInMemory);
810     Process->StackCount--;
811 
812     /* Check if we can swap the process out */
813     if (!Process->StackCount)
814     {
815         /* FIXME: Swap the process out */
816     }
817 
818     /* Release dispatcher lock */
819     KiReleaseDispatcherLockFromSynchLevel();
820 
821     /* Check if there's an APC state to restore */
822     if (ApcState->Process)
823     {
824         /* Restore the APC State */
825         KiMoveApcState(ApcState, &Thread->ApcState);
826     }
827     else
828     {
829         /* The ApcState parameter is useless, so use the saved data and reset it */
830         KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
831         Thread->SavedApcState.Process = NULL;
832         Thread->ApcStateIndex = OriginalApcEnvironment;
833         Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
834         Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
835     }
836 
837     /* Release lock */
838     KiReleaseApcLockFromSynchLevel(&ApcLock);
839 
840     /* Swap Processes */
841     KiSwapProcess(Thread->ApcState.Process, Process);
842 
843     /* Exit the dispatcher */
844     KiExitDispatcher(ApcLock.OldIrql);
845 
846     /* Check if we have pending APCs */
847     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
848     {
849         /* What do you know, we do! Request them to be delivered */
850         Thread->ApcState.KernelApcPending = TRUE;
851         HalRequestSoftwareInterrupt(APC_LEVEL);
852     }
853 }
854 
855 /*
856  * @implemented
857  */
858 ULONG
859 NTAPI
860 KeQueryRuntimeProcess(IN PKPROCESS Process,
861                       OUT PULONG UserTime)
862 {
863     ULONG TotalUser, TotalKernel;
864     KLOCK_QUEUE_HANDLE ProcessLock;
865     PLIST_ENTRY NextEntry, ListHead;
866     PKTHREAD Thread;
867 
868     ASSERT_PROCESS(Process);
869 
870     /* Initialize user and kernel times */
871     TotalUser = Process->UserTime;
872     TotalKernel = Process->KernelTime;
873 
874     /* Lock the process */
875     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
876 
877     /* Loop all child threads and sum up their times */
878     ListHead = &Process->ThreadListHead;
879     NextEntry = ListHead->Flink;
880     while (ListHead != NextEntry)
881     {
882         /* Get the thread */
883         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
884 
885         /* Sum up times */
886         TotalKernel += Thread->KernelTime;
887         TotalUser += Thread->UserTime;
888 
889         /* Go to the next one */
890         NextEntry = NextEntry->Flink;
891     }
892 
893     /* Release lock */
894     KiReleaseProcessLock(&ProcessLock);
895 
896     /* Return the user time */
897     *UserTime = TotalUser;
898 
899     /* Return the kernel time */
900     return TotalKernel;
901 }
902 
903 /*
904  * @implemented
905  */
906 BOOLEAN
907 NTAPI
908 KeAddSystemServiceTable(IN PULONG_PTR Base,
909                         IN PULONG Count OPTIONAL,
910                         IN ULONG Limit,
911                         IN PUCHAR Number,
912                         IN ULONG Index)
913 {
914     PAGED_CODE();
915 
916     /* Check if descriptor table entry is free */
917     if ((Index > SSDT_MAX_ENTRIES - 1) ||
918         (KeServiceDescriptorTable[Index].Base) ||
919         (KeServiceDescriptorTableShadow[Index].Base))
920     {
921         /* It's not, fail */
922         return FALSE;
923     }
924 
925     /* Initialize the shadow service descriptor table */
926     KeServiceDescriptorTableShadow[Index].Base = Base;
927     KeServiceDescriptorTableShadow[Index].Limit = Limit;
928     KeServiceDescriptorTableShadow[Index].Number = Number;
929     KeServiceDescriptorTableShadow[Index].Count = Count;
930     return TRUE;
931 }
932 
933 /*
934  * @implemented
935  */
936 BOOLEAN
937 NTAPI
938 KeRemoveSystemServiceTable(IN ULONG Index)
939 {
940     PAGED_CODE();
941 
942     /* Make sure the Index is valid */
943     if (Index > (SSDT_MAX_ENTRIES - 1)) return FALSE;
944 
945     /* Is there a Normal Descriptor Table? */
946     if (!KeServiceDescriptorTable[Index].Base)
947     {
948         /* Not with the index, is there a shadow at least? */
949         if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
950     }
951 
952     /* Now clear from the Shadow Table. */
953     KeServiceDescriptorTableShadow[Index].Base = NULL;
954     KeServiceDescriptorTableShadow[Index].Number = NULL;
955     KeServiceDescriptorTableShadow[Index].Limit = 0;
956     KeServiceDescriptorTableShadow[Index].Count = NULL;
957 
958     /* Check if we should clean from the Master one too */
959     if (Index == 1)
960     {
961         KeServiceDescriptorTable[Index].Base = NULL;
962         KeServiceDescriptorTable[Index].Number = NULL;
963         KeServiceDescriptorTable[Index].Limit = 0;
964         KeServiceDescriptorTable[Index].Count = NULL;
965     }
966 
967     /* Return success */
968     return TRUE;
969 }
970 /* EOF */
971