Big merge: thanks alex and greatlord. Not a complete merge but most
[reactos.git] / reactos / ntoskrnl / ke / process.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ke/process.c
5 * PURPOSE: Kernel Process Management and System Call Tables
6 * PROGRAMMERS: Alex Ionescu
7 * Gregor Anich
8 */
9
10 /* INCLUDES ********(*********************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/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 /* FUNCTIONS *****************************************************************/
32
33 PKPROCESS
34 STDCALL
35 KeGetCurrentProcess(VOID)
36 {
37 return(&(PsGetCurrentProcess()->Pcb));
38 }
39
40 static __inline
41 VOID
42 NTAPI
43 UpdatePageDirs(IN PKTHREAD Thread,
44 IN PKPROCESS Process)
45 {
46 /*
47 * The stack and the thread structure of the current process may be
48 * located in a page which is not present in the page directory of
49 * the process we're attaching to. That would lead to a page fault
50 * when this function returns. However, since the processor can't
51 * call the page fault handler 'cause it can't push EIP on the stack,
52 * this will show up as a stack fault which will crash the entire system.
53 * To prevent this, make sure the page directory of the process we're
54 * attaching to is up-to-date.
55 */
56 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, KERNEL_STACK_SIZE);
57 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
58 }
59
60 VOID
61 NTAPI
62 KiAttachProcess(PKTHREAD Thread,
63 PKPROCESS Process,
64 KIRQL OldIrql,
65 PRKAPC_STATE SavedApcState)
66 {
67 ASSERT(Process != Thread->ApcState.Process);
68 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n",
69 Thread, Process, SavedApcState);
70
71 /* Increase Stack Count */
72 Process->StackCount++;
73
74 /* Swap the APC Environment */
75 KiMoveApcState(&Thread->ApcState, SavedApcState);
76
77 /* Reinitialize Apc State */
78 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
79 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
80 Thread->ApcState.Process = Process;
81 Thread->ApcState.KernelApcInProgress = FALSE;
82 Thread->ApcState.KernelApcPending = FALSE;
83 Thread->ApcState.UserApcPending = FALSE;
84
85 /* Update Environment Pointers if needed*/
86 if (SavedApcState == &Thread->SavedApcState)
87 {
88 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
89 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
90 Thread->ApcStateIndex = AttachedApcEnvironment;
91 }
92
93 /* Check if the process is paged in */
94 if (Process->State == ProcessInMemory)
95 {
96 /* FIXME: Scan the Ready Thread List once new scheduler is in */
97
98 /* Swap the Processes */
99 KiSwapProcess(Process, SavedApcState->Process);
100
101 /* Return to old IRQL*/
102 KeReleaseDispatcherDatabaseLock(OldIrql);
103 }
104 else
105 {
106 DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
107 DbgBreakPoint();
108 }
109 }
110
111 VOID
112 NTAPI
113 KeInitializeProcess(IN OUT PKPROCESS Process,
114 IN KPRIORITY Priority,
115 IN KAFFINITY Affinity,
116 IN LARGE_INTEGER DirectoryTableBase)
117 {
118 ULONG i = 0;
119 UCHAR IdealNode = 0;
120 PKNODE Node;
121
122 /* Initialize the Dispatcher Header */
123 KeInitializeDispatcherHeader(&Process->Header,
124 ProcessObject,
125 sizeof(KPROCESS),
126 FALSE);
127
128 /* Initialize Scheduler Data, Alignment Faults and Set the PDE */
129 Process->Affinity = Affinity;
130 Process->BasePriority = (CHAR)Priority;
131 Process->QuantumReset = 6;
132 Process->DirectoryTableBase = DirectoryTableBase;
133 Process->AutoAlignment = TRUE;
134 #ifdef _M_IX86
135 Process->IopmOffset = 0xFFFF;
136 #endif
137
138 /* Initialize the lists */
139 InitializeListHead(&Process->ThreadListHead);
140 InitializeListHead(&Process->ProfileListHead);
141 InitializeListHead(&Process->ReadyListHead);
142
143 /* Initialize the current State */
144 Process->State = ProcessInMemory;
145
146 /* Check how many Nodes there are on the system */
147 if (KeNumberNodes > 1)
148 {
149 /* Set the new seed */
150 KeProcessNodeSeed = (KeProcessNodeSeed + 1) / KeNumberNodes;
151 IdealNode = KeProcessNodeSeed;
152
153 /* Loop every node */
154 do
155 {
156 /* Check if the affinity matches */
157 if (KeNodeBlock[IdealNode]->ProcessorMask != Affinity) break;
158
159 /* No match, try next Ideal Node and increase node loop index */
160 IdealNode++;
161 i++;
162
163 /* Check if the Ideal Node is beyond the total number of nodes */
164 if (IdealNode >= KeNumberNodes)
165 {
166 /* Normalize the Ideal Node */
167 IdealNode -= KeNumberNodes;
168 }
169 } while (i < KeNumberNodes);
170 }
171
172 /* Set the ideal node and get the ideal node block */
173 Process->IdealNode = IdealNode;
174 Node = KeNodeBlock[IdealNode];
175 ASSERT(Node->ProcessorMask & Affinity);
176
177 /* Find the matching affinity set to calculate the thread seed */
178 Affinity &= Node->ProcessorMask;
179 Process->ThreadSeed = KeFindNextRightSetAffinity(Node->Seed,
180 (ULONG)Affinity);
181 Node->Seed = Process->ThreadSeed;
182 }
183
184 ULONG
185 NTAPI
186 KeSetProcess(PKPROCESS Process,
187 KPRIORITY Increment,
188 BOOLEAN InWait)
189 {
190 KIRQL OldIrql;
191 ULONG OldState;
192
193 /* Lock Dispatcher */
194 OldIrql = KeAcquireDispatcherDatabaseLock();
195
196 /* Get Old State */
197 OldState = Process->Header.SignalState;
198
199 /* Signal the Process */
200 Process->Header.SignalState = TRUE;
201 if ((OldState == 0) && IsListEmpty(&Process->Header.WaitListHead) != TRUE)
202 {
203 /* Satisfy waits */
204 KiWaitTest((PVOID)Process, Increment);
205 }
206
207 /* Release Dispatcher Database */
208 KeReleaseDispatcherDatabaseLock(OldIrql);
209
210 /* Return the previous State */
211 return OldState;
212 }
213
214 VOID
215 NTAPI
216 KiSwapProcess(PKPROCESS NewProcess,
217 PKPROCESS OldProcess)
218 {
219 #ifdef _M_IX86
220 DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart);
221 Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart);
222 #endif
223 }
224
225 VOID
226 NTAPI
227 KeSetQuantumProcess(IN PKPROCESS Process,
228 IN UCHAR Quantum)
229 {
230 KIRQL OldIrql;
231 PLIST_ENTRY NextEntry, ListHead;
232 PKTHREAD Thread;
233 ASSERT_PROCESS(Process);
234 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
235
236 /* Lock Dispatcher */
237 OldIrql = KeAcquireDispatcherDatabaseLock();
238
239 /* Set new quantum */
240 Process->QuantumReset = Quantum;
241
242 /* Loop all child threads */
243 ListHead = &Process->ThreadListHead;
244 NextEntry = ListHead->Flink;
245 while (ListHead != NextEntry)
246 {
247 /* Get the thread */
248 Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
249
250 /* Set quantum */
251 Thread->QuantumReset = Quantum;
252
253 /* Go to the next one */
254 NextEntry = NextEntry->Flink;
255 }
256
257 /* Release Dispatcher Database */
258 KeReleaseDispatcherDatabaseLock(OldIrql);
259 }
260
261 KPRIORITY
262 NTAPI
263 KeSetPriorityAndQuantumProcess(IN PKPROCESS Process,
264 IN KPRIORITY Priority,
265 IN UCHAR Quantum OPTIONAL)
266 {
267 KPRIORITY Delta;
268 PLIST_ENTRY NextEntry, ListHead;
269 KPRIORITY NewPriority, OldPriority;
270 KIRQL OldIrql;
271 PKTHREAD Thread;
272 BOOLEAN Released;
273 ASSERT_PROCESS(Process);
274 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
275
276 /* Check if the process already has this priority */
277 if (Process->BasePriority == Priority)
278 {
279 /* Don't change anything */
280 return Process->BasePriority;
281 }
282
283 /* If the caller gave priority 0, normalize to 1 */
284 if (!Priority) Priority = 1;
285
286 /* Lock Dispatcher */
287 OldIrql = KeAcquireDispatcherDatabaseLock();
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 = 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 /* Calculate the new priority */
316 NewPriority = Thread->BasePriority + Delta;
317 if (NewPriority < LOW_REALTIME_PRIORITY)
318 {
319 /* We're in real-time range, don't let it go below */
320 NewPriority = LOW_REALTIME_PRIORITY;
321 }
322 else if (NewPriority > HIGH_PRIORITY)
323 {
324 /* We're going beyond the maximum priority, normalize */
325 NewPriority = HIGH_PRIORITY;
326 }
327
328 /*
329 * If priority saturation occured or the old priority was still in
330 * the real-time range, don't do anything.
331 */
332 if (!(Thread->Saturation) || (OldPriority < LOW_REALTIME_PRIORITY))
333 {
334 /* Check if we had priority saturation */
335 if (Thread->Saturation > 0)
336 {
337 /* Boost priority to maximum */
338 NewPriority = HIGH_PRIORITY;
339 }
340 else if (Thread->Saturation < 0)
341 {
342 /* If we had negative saturation, set minimum priority */
343 NewPriority = LOW_REALTIME_PRIORITY;
344 }
345
346 /* Update priority and quantum */
347 Thread->BasePriority = NewPriority;
348 Thread->Quantum = Thread->QuantumReset;
349
350 /* Disable decrements and update priority */
351 Thread->PriorityDecrement = 0;
352 KiSetPriorityThread(Thread, NewPriority, &Released);
353 }
354
355 /* Go to the next thread */
356 NextEntry = NextEntry->Flink;
357 }
358 }
359 else
360 {
361 /* Loop the thread list */
362 while (NextEntry != ListHead)
363 {
364 /* Get the thread */
365 Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry);
366
367 /* Update the quantum if we had one */
368 if (Quantum) Thread->QuantumReset = Quantum;
369
370 /* Calculate the new priority */
371 NewPriority = Thread->BasePriority + Delta;
372 if (NewPriority >= LOW_REALTIME_PRIORITY)
373 {
374 /* We're not real-time range, don't let it enter RT range */
375 NewPriority = LOW_REALTIME_PRIORITY - 1;
376 }
377 else if (NewPriority <= LOW_PRIORITY)
378 {
379 /* We're going below the minimum priority, normalize */
380 NewPriority = 1;
381 }
382
383 /*
384 * If priority saturation occured or the old priority was still in
385 * the real-time range, don't do anything.
386 */
387 if (!(Thread->Saturation) ||
388 (OldPriority >= LOW_REALTIME_PRIORITY))
389 {
390 /* Check if we had priority saturation */
391 if (Thread->Saturation > 0)
392 {
393 /* Boost priority to maximum */
394 NewPriority = LOW_REALTIME_PRIORITY - 1;
395 }
396 else if (Thread->Saturation < 0)
397 {
398 /* If we had negative saturation, set minimum priority */
399 NewPriority = 1;
400 }
401
402 /* Update priority and quantum */
403 Thread->BasePriority = NewPriority;
404 Thread->Quantum = Thread->QuantumReset;
405
406 /* Disable decrements and update priority */
407 Thread->PriorityDecrement = 0;
408 KiSetPriorityThread(Thread, NewPriority, &Released);
409 }
410
411 /* Go to the next thread */
412 NextEntry = NextEntry->Flink;
413 }
414 }
415
416 /* Release Dispatcher Database */
417 if (!Released) KeReleaseDispatcherDatabaseLock(OldIrql);
418
419 /* Return previous priority */
420 return OldPriority;
421 }
422
423 /*
424 * @implemented
425 */
426 VOID
427 NTAPI
428 KeAttachProcess(PKPROCESS Process)
429 {
430 KIRQL OldIrql;
431 PKTHREAD Thread;
432 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
433 DPRINT("KeAttachProcess: %x\n", Process);
434
435 /* Make sure that we are in the right page directory */
436 Thread = KeGetCurrentThread();
437 UpdatePageDirs(Thread, Process);
438
439 /* Lock Dispatcher */
440 OldIrql = KeAcquireDispatcherDatabaseLock();
441
442 /* Check if we're already in that process */
443 if (Thread->ApcState.Process == Process)
444 {
445 /* Unlock the dispatcher, nothing to do */
446 KeReleaseDispatcherDatabaseLock(OldIrql);
447 }
448 else if ((Thread->ApcStateIndex != OriginalApcEnvironment) ||
449 (KeIsExecutingDpc()))
450 {
451 /* Executing a DPC or already attached, crash! */
452 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
453 (ULONG_PTR)Process,
454 (ULONG_PTR)Thread->ApcState.Process,
455 Thread->ApcStateIndex,
456 KeIsExecutingDpc());
457 }
458 else
459 {
460 /* Legit attach attempt: do it! */
461 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
462 }
463 }
464
465 /*
466 * @implemented
467 */
468 VOID
469 NTAPI
470 KeDetachProcess (VOID)
471 {
472 PKTHREAD Thread;
473 KIRQL OldIrql;
474 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
475 DPRINT("KeDetachProcess()\n");
476
477 /* Get Current Thread and lock the dispatcher */
478 Thread = KeGetCurrentThread();
479 OldIrql = KeAcquireDispatcherDatabaseLock();
480
481 /* Check if it's attached */
482 if (Thread->ApcStateIndex != OriginalApcEnvironment)
483 {
484 /* It is, decrease Stack Count */
485 if(!(--Thread->ApcState.Process->StackCount))
486 {
487 /* FIXME: Swap the process out */
488 }
489
490 /* Restore the APC State */
491 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
492 Thread->SavedApcState.Process = NULL;
493 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
494 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
495 Thread->ApcStateIndex = OriginalApcEnvironment;
496
497 /* Check if we have pending APCs */
498 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
499 {
500 /* What do you know, we do! Request them to be delivered */
501 Thread->ApcState.KernelApcPending = TRUE;
502 HalRequestSoftwareInterrupt(APC_LEVEL);
503 }
504
505 /* Swap Processes */
506 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
507 }
508
509 /* Unlock Dispatcher */
510 KeReleaseDispatcherDatabaseLock(OldIrql);
511 }
512
513 /*
514 * @implemented
515 */
516 BOOLEAN
517 NTAPI
518 KeIsAttachedProcess(VOID)
519 {
520 /* Return the APC State */
521 return KeGetCurrentThread()->ApcStateIndex;
522 }
523
524 /*
525 * @implemented
526 */
527 VOID
528 NTAPI
529 KeStackAttachProcess(IN PKPROCESS Process,
530 OUT PRKAPC_STATE ApcState)
531 {
532 KIRQL OldIrql;
533 PKTHREAD Thread;
534 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
535
536 /* Make sure that we are in the right page directory */
537 Thread = KeGetCurrentThread();
538 UpdatePageDirs(Thread, Process);
539
540 /* Acquire the dispatcher lock */
541 OldIrql = KeAcquireDispatcherDatabaseLock();
542
543 /* Crash system if DPC is being executed! */
544 if (KeIsExecutingDpc())
545 {
546 /* Executing a DPC, crash! */
547 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
548 (ULONG_PTR)Process,
549 (ULONG_PTR)Thread->ApcState.Process,
550 Thread->ApcStateIndex,
551 KeIsExecutingDpc());
552 }
553
554 /* Check if we are already in the target process */
555 if (Thread->ApcState.Process == Process)
556 {
557 /* Unlock the dispatcher database */
558 KeReleaseDispatcherDatabaseLock(OldIrql);
559
560 /* Set magic value so we don't crash later when detaching */
561 ApcState->Process = (PKPROCESS)1;
562 }
563 else
564 {
565 /* Check if the Current Thread is already attached */
566 if (Thread->ApcStateIndex != OriginalApcEnvironment)
567 {
568 /* We're already attached, so save the APC State into what we got */
569 KiAttachProcess(Thread, Process, OldIrql, ApcState);
570 }
571 else
572 {
573 /* We're not attached, so save the APC State into SavedApcState */
574 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
575 ApcState->Process = NULL;
576 }
577 }
578 }
579
580 /*
581 * @implemented
582 */
583 VOID
584 NTAPI
585 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
586 {
587 KIRQL OldIrql;
588 PKTHREAD Thread;
589 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
590
591 /* Get the current thread and acquire the dispatcher lock */
592 Thread = KeGetCurrentThread();
593 OldIrql = KeAcquireDispatcherDatabaseLock();
594
595 /* Check for magic value meaning we were already in the same process */
596 if (ApcState->Process != (PKPROCESS)1)
597 {
598 /*
599 * Check if the process isn't attacked, or has a Kernel APC in progress
600 * or has pending APC of any kind.
601 */
602 if ((Thread->ApcStateIndex == OriginalApcEnvironment) ||
603 (Thread->ApcState.KernelApcInProgress) ||
604 (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
605 (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
606 {
607 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
608 }
609
610 /* Decrease Stack Count */
611 if(!(--Thread->ApcState.Process->StackCount))
612 {
613 /* FIXME: Swap the process out */
614 }
615
616 if (ApcState->Process != NULL)
617 {
618 /* Restore the APC State */
619 KiMoveApcState(ApcState, &Thread->ApcState);
620 }
621 else
622 {
623 /* The ApcState parameter is useless, so use the saved data and reset it */
624 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
625 Thread->SavedApcState.Process = NULL;
626 Thread->ApcStateIndex = OriginalApcEnvironment;
627 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
628 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
629 }
630
631 /* Check if we have pending APCs */
632 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
633 {
634 /* What do you know, we do! Request them to be delivered */
635 Thread->ApcState.KernelApcPending = TRUE;
636 HalRequestSoftwareInterrupt(APC_LEVEL);
637 }
638
639 /* Swap Processes */
640 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
641 }
642
643 /* Return to old IRQL*/
644 KeReleaseDispatcherDatabaseLock(OldIrql);
645 }
646
647 /*
648 * @implemented
649 */
650 BOOLEAN
651 NTAPI
652 KeAddSystemServiceTable(PULONG_PTR Base,
653 PULONG Count OPTIONAL,
654 ULONG Limit,
655 PUCHAR Number,
656 ULONG Index)
657 {
658 /* Check if descriptor table entry is free */
659 if ((Index > SSDT_MAX_ENTRIES - 1) ||
660 (KeServiceDescriptorTable[Index].Base) ||
661 (KeServiceDescriptorTableShadow[Index].Base))
662 {
663 return FALSE;
664 }
665
666 /* Initialize the shadow service descriptor table */
667 KeServiceDescriptorTableShadow[Index].Base = Base;
668 KeServiceDescriptorTableShadow[Index].Limit = Limit;
669 KeServiceDescriptorTableShadow[Index].Number = Number;
670 KeServiceDescriptorTableShadow[Index].Count = Count;
671
672 return TRUE;
673 }
674
675 /*
676 * @implemented
677 */
678 BOOLEAN
679 NTAPI
680 KeRemoveSystemServiceTable(IN ULONG Index)
681 {
682 /* Make sure the Index is valid */
683 if (Index > SSDT_MAX_ENTRIES - 1) return FALSE;
684
685 /* Is there a Normal Descriptor Table? */
686 if (!KeServiceDescriptorTable[Index].Base)
687 {
688 /* Not with the index, is there a shadow at least? */
689 if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
690 }
691
692 /* Now clear from the Shadow Table. */
693 KeServiceDescriptorTableShadow[Index].Base = NULL;
694 KeServiceDescriptorTableShadow[Index].Number = NULL;
695 KeServiceDescriptorTableShadow[Index].Limit = 0;
696 KeServiceDescriptorTableShadow[Index].Count = NULL;
697
698 /* Check if we should clean from the Master one too */
699 if (Index == 1)
700 {
701 KeServiceDescriptorTable[Index].Base = NULL;
702 KeServiceDescriptorTable[Index].Number = NULL;
703 KeServiceDescriptorTable[Index].Limit = 0;
704 KeServiceDescriptorTable[Index].Count = NULL;
705 }
706
707 return TRUE;
708 }
709 /* EOF */