xref: /reactos/ntoskrnl/ke/procobj.c (revision 7115d7ba)
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     /* 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     KiReleaseDispatcherLockFromSynchLevel();
511 
512     /* Release the process lock */
513     KiReleaseProcessLockFromSynchLevel(&ProcessLock);
514     KiExitDispatcher(ProcessLock.OldIrql);
515 
516     /* Return previous priority */
517     return OldPriority;
518 }
519 
520 VOID
521 NTAPI
522 KeQueryValuesProcess(IN PKPROCESS Process,
523                      PPROCESS_VALUES Values)
524 {
525     PEPROCESS EProcess;
526     PLIST_ENTRY NextEntry;
527     ULONG TotalKernel, TotalUser;
528     KLOCK_QUEUE_HANDLE ProcessLock;
529 
530     ASSERT_PROCESS(Process);
531     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
532 
533     /* Lock the process */
534     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
535 
536     /* Initialize user and kernel times */
537     TotalKernel = Process->KernelTime;
538     TotalUser = Process->UserTime;
539 
540     /* Copy the IO_COUNTERS from the process */
541     EProcess = (PEPROCESS)Process;
542     Values->IoInfo.ReadOperationCount = EProcess->ReadOperationCount.QuadPart;
543     Values->IoInfo.WriteOperationCount = EProcess->WriteOperationCount.QuadPart;
544     Values->IoInfo.OtherOperationCount = EProcess->OtherOperationCount.QuadPart;
545     Values->IoInfo.ReadTransferCount = EProcess->ReadTransferCount.QuadPart;
546     Values->IoInfo.WriteTransferCount = EProcess->WriteTransferCount.QuadPart;
547     Values->IoInfo.OtherTransferCount = EProcess->OtherTransferCount.QuadPart;
548 
549     /* Loop all child threads and sum up their times */
550     for (NextEntry = Process->ThreadListHead.Flink;
551          NextEntry != &Process->ThreadListHead;
552          NextEntry = NextEntry->Flink)
553     {
554         PKTHREAD Thread;
555 
556         /* Get the thread */
557         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
558 
559         /* Sum up times */
560         TotalKernel += Thread->KernelTime;
561         TotalUser += Thread->UserTime;
562     }
563 
564     /* Release the process lock */
565     KiReleaseProcessLock(&ProcessLock);
566 
567     /* Compute total times */
568     Values->TotalKernelTime.QuadPart = TotalKernel * (LONGLONG)KeMaximumIncrement;
569     Values->TotalUserTime.QuadPart = TotalUser * (LONGLONG)KeMaximumIncrement;
570 }
571 
572 /* PUBLIC FUNCTIONS **********************************************************/
573 
574 /*
575  * @implemented
576  */
577 VOID
578 NTAPI
579 KeAttachProcess(IN PKPROCESS Process)
580 {
581     KLOCK_QUEUE_HANDLE ApcLock;
582     PKTHREAD Thread = KeGetCurrentThread();
583     ASSERT_PROCESS(Process);
584     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
585 
586     /* Check if we're already in that process */
587     if (Thread->ApcState.Process == Process) return;
588 
589     /* Check if a DPC is executing or if we're already attached */
590     if ((Thread->ApcStateIndex != OriginalApcEnvironment) ||
591         (KeIsExecutingDpc()))
592     {
593         /* Invalid attempt */
594         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
595                      (ULONG_PTR)Process,
596                      (ULONG_PTR)Thread->ApcState.Process,
597                      Thread->ApcStateIndex,
598                      KeIsExecutingDpc());
599     }
600     else
601     {
602         /* Acquire APC Lock */
603         KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
604 
605         /* Acquire the dispatcher lock */
606         KiAcquireDispatcherLockAtSynchLevel();
607 
608         /* Legit attach attempt: do it! */
609         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
610     }
611 }
612 
613 /*
614  * @implemented
615  */
616 VOID
617 NTAPI
618 KeDetachProcess(VOID)
619 {
620     PKTHREAD Thread = KeGetCurrentThread();
621     KLOCK_QUEUE_HANDLE ApcLock;
622     PKPROCESS Process;
623     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
624 
625     /* Check if it's attached */
626     if (Thread->ApcStateIndex == OriginalApcEnvironment) return;
627 
628     /* Acquire APC Lock */
629     KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
630 
631     /* Check for invalid attach attempts */
632     if ((Thread->ApcState.KernelApcInProgress) ||
633         !(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
634         !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
635     {
636         /* Crash the system */
637         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
638     }
639 
640     /* Get the process */
641     Process = Thread->ApcState.Process;
642 
643     /* Acquire dispatcher lock */
644     KiAcquireDispatcherLockAtSynchLevel();
645 
646     /* Decrease the stack count */
647     ASSERT(Process->StackCount != 0);
648     ASSERT(Process->State == ProcessInMemory);
649     Process->StackCount--;
650 
651     /* Check if we can swap the process out */
652     if (!Process->StackCount)
653     {
654         /* FIXME: Swap the process out */
655     }
656 
657     /* Release dispatcher lock */
658     KiReleaseDispatcherLockFromSynchLevel();
659 
660     /* Restore the APC State */
661     KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
662     Thread->SavedApcState.Process = NULL;
663     Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
664     Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
665     Thread->ApcStateIndex = OriginalApcEnvironment;
666 
667     /* Release lock */
668     KiReleaseApcLockFromSynchLevel(&ApcLock);
669 
670     /* Swap Processes */
671     KiSwapProcess(Thread->ApcState.Process, Process);
672 
673     /* Exit the dispatcher */
674     KiExitDispatcher(ApcLock.OldIrql);
675 
676     /* Check if we have pending APCs */
677     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
678     {
679         /* What do you know, we do! Request them to be delivered */
680         Thread->ApcState.KernelApcPending = TRUE;
681         HalRequestSoftwareInterrupt(APC_LEVEL);
682     }
683 }
684 
685 /*
686  * @implemented
687  */
688 BOOLEAN
689 NTAPI
690 KeIsAttachedProcess(VOID)
691 {
692     /* Return the APC State */
693     return KeGetCurrentThread()->ApcStateIndex;
694 }
695 
696 /*
697  * @implemented
698  */
699 VOID
700 NTAPI
701 KeStackAttachProcess(IN PKPROCESS Process,
702                      OUT PRKAPC_STATE ApcState)
703 {
704     KLOCK_QUEUE_HANDLE ApcLock;
705     PKTHREAD Thread = KeGetCurrentThread();
706     ASSERT_PROCESS(Process);
707     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
708 
709     /* Crash system if DPC is being executed! */
710     if (KeIsExecutingDpc())
711     {
712         /* Executing a DPC, crash! */
713         KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
714                      (ULONG_PTR)Process,
715                      (ULONG_PTR)Thread->ApcState.Process,
716                      Thread->ApcStateIndex,
717                      KeIsExecutingDpc());
718     }
719 
720     /* Check if we are already in the target process */
721     if (Thread->ApcState.Process == Process)
722     {
723         /* Set magic value so we don't crash later when detaching */
724         ApcState->Process = (PKPROCESS)1;
725         return;
726     }
727 
728     /* Acquire APC Lock */
729     KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
730 
731     /* Acquire dispatcher lock */
732     KiAcquireDispatcherLockAtSynchLevel();
733 
734     /* Check if the Current Thread is already attached */
735     if (Thread->ApcStateIndex != OriginalApcEnvironment)
736     {
737         /* We're already attached, so save the APC State into what we got */
738         KiAttachProcess(Thread, Process, &ApcLock, ApcState);
739     }
740     else
741     {
742         /* We're not attached, so save the APC State into SavedApcState */
743         KiAttachProcess(Thread, Process, &ApcLock, &Thread->SavedApcState);
744         ApcState->Process = NULL;
745     }
746 }
747 
748 /*
749  * @implemented
750  */
751 VOID
752 NTAPI
753 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
754 {
755     KLOCK_QUEUE_HANDLE ApcLock;
756     PKTHREAD Thread = KeGetCurrentThread();
757     PKPROCESS Process;
758     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
759 
760     /* Check for magic value meaning we were already in the same process */
761     if (ApcState->Process == (PKPROCESS)1) return;
762 
763     /* Loop to make sure no APCs are pending  */
764     for (;;)
765     {
766         /* Acquire APC Lock */
767         KiAcquireApcLockRaiseToSynch(Thread, &ApcLock);
768 
769         /* Check if a kernel APC is pending */
770         if (Thread->ApcState.KernelApcPending)
771         {
772             /* Check if kernel APC should be delivered */
773             if (!(Thread->KernelApcDisable) && (ApcLock.OldIrql <= APC_LEVEL))
774             {
775                 /* Release the APC lock so that the APC can be delivered */
776                 KiReleaseApcLock(&ApcLock);
777                 continue;
778             }
779         }
780 
781         /* Otherwise, break out */
782         break;
783     }
784 
785     /*
786      * Check if the process isn't attacked, or has a Kernel APC in progress
787      * or has pending APC of any kind.
788      */
789     if ((Thread->ApcStateIndex == OriginalApcEnvironment) ||
790         (Thread->ApcState.KernelApcInProgress) ||
791         (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
792         (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
793     {
794         /* Bugcheck the system */
795         KeBugCheck(INVALID_PROCESS_DETACH_ATTEMPT);
796     }
797 
798     /* Get the process */
799     Process = Thread->ApcState.Process;
800 
801     /* Acquire dispatcher lock */
802     KiAcquireDispatcherLockAtSynchLevel();
803 
804     /* Decrease the stack count */
805     ASSERT(Process->StackCount != 0);
806     ASSERT(Process->State == ProcessInMemory);
807     Process->StackCount--;
808 
809     /* Check if we can swap the process out */
810     if (!Process->StackCount)
811     {
812         /* FIXME: Swap the process out */
813     }
814 
815     /* Release dispatcher lock */
816     KiReleaseDispatcherLockFromSynchLevel();
817 
818     /* Check if there's an APC state to restore */
819     if (ApcState->Process)
820     {
821         /* Restore the APC State */
822         KiMoveApcState(ApcState, &Thread->ApcState);
823     }
824     else
825     {
826         /* The ApcState parameter is useless, so use the saved data and reset it */
827         KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
828         Thread->SavedApcState.Process = NULL;
829         Thread->ApcStateIndex = OriginalApcEnvironment;
830         Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
831         Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
832     }
833 
834     /* Release lock */
835     KiReleaseApcLockFromSynchLevel(&ApcLock);
836 
837     /* Swap Processes */
838     KiSwapProcess(Thread->ApcState.Process, Process);
839 
840     /* Exit the dispatcher */
841     KiExitDispatcher(ApcLock.OldIrql);
842 
843     /* Check if we have pending APCs */
844     if (!(IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])))
845     {
846         /* What do you know, we do! Request them to be delivered */
847         Thread->ApcState.KernelApcPending = TRUE;
848         HalRequestSoftwareInterrupt(APC_LEVEL);
849     }
850 }
851 
852 /*
853  * @implemented
854  */
855 ULONG
856 NTAPI
857 KeQueryRuntimeProcess(IN PKPROCESS Process,
858                       OUT PULONG UserTime)
859 {
860     ULONG TotalUser, TotalKernel;
861     KLOCK_QUEUE_HANDLE ProcessLock;
862     PLIST_ENTRY NextEntry, ListHead;
863     PKTHREAD Thread;
864 
865     ASSERT_PROCESS(Process);
866 
867     /* Initialize user and kernel times */
868     TotalUser = Process->UserTime;
869     TotalKernel = Process->KernelTime;
870 
871     /* Lock the process */
872     KiAcquireProcessLockRaiseToSynch(Process, &ProcessLock);
873 
874     /* Loop all child threads and sum up their times */
875     ListHead = &Process->ThreadListHead;
876     NextEntry = ListHead->Flink;
877     while (ListHead != NextEntry)
878     {
879         /* Get the thread */
880         Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
881 
882         /* Sum up times */
883         TotalKernel += Thread->KernelTime;
884         TotalUser += Thread->UserTime;
885 
886         /* Go to the next one */
887         NextEntry = NextEntry->Flink;
888     }
889 
890     /* Release lock */
891     KiReleaseProcessLock(&ProcessLock);
892 
893     /* Return the user time */
894     *UserTime = TotalUser;
895 
896     /* Return the kernel time */
897     return TotalKernel;
898 }
899 
900 /*
901  * @implemented
902  */
903 BOOLEAN
904 NTAPI
905 KeAddSystemServiceTable(IN PULONG_PTR Base,
906                         IN PULONG Count OPTIONAL,
907                         IN ULONG Limit,
908                         IN PUCHAR Number,
909                         IN ULONG Index)
910 {
911     PAGED_CODE();
912 
913     /* Check if descriptor table entry is free */
914     if ((Index > SSDT_MAX_ENTRIES - 1) ||
915         (KeServiceDescriptorTable[Index].Base) ||
916         (KeServiceDescriptorTableShadow[Index].Base))
917     {
918         /* It's not, fail */
919         return FALSE;
920     }
921 
922     /* Initialize the shadow service descriptor table */
923     KeServiceDescriptorTableShadow[Index].Base = Base;
924     KeServiceDescriptorTableShadow[Index].Limit = Limit;
925     KeServiceDescriptorTableShadow[Index].Number = Number;
926     KeServiceDescriptorTableShadow[Index].Count = Count;
927     return TRUE;
928 }
929 
930 /*
931  * @implemented
932  */
933 BOOLEAN
934 NTAPI
935 KeRemoveSystemServiceTable(IN ULONG Index)
936 {
937     PAGED_CODE();
938 
939     /* Make sure the Index is valid */
940     if (Index > (SSDT_MAX_ENTRIES - 1)) return FALSE;
941 
942     /* Is there a Normal Descriptor Table? */
943     if (!KeServiceDescriptorTable[Index].Base)
944     {
945         /* Not with the index, is there a shadow at least? */
946         if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
947     }
948 
949     /* Now clear from the Shadow Table. */
950     KeServiceDescriptorTableShadow[Index].Base = NULL;
951     KeServiceDescriptorTableShadow[Index].Number = NULL;
952     KeServiceDescriptorTableShadow[Index].Limit = 0;
953     KeServiceDescriptorTableShadow[Index].Count = NULL;
954 
955     /* Check if we should clean from the Master one too */
956     if (Index == 1)
957     {
958         KeServiceDescriptorTable[Index].Base = NULL;
959         KeServiceDescriptorTable[Index].Number = NULL;
960         KeServiceDescriptorTable[Index].Limit = 0;
961         KeServiceDescriptorTable[Index].Count = NULL;
962     }
963 
964     /* Return success */
965     return TRUE;
966 }
967 /* EOF */
968