Of course, I forgot to commit the new file in revision 22049...
[reactos.git] / reactos / ntoskrnl / ps / thread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
6 *
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 * Phillip Susi
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS ******************************************************************/
18
19 extern LIST_ENTRY PsActiveProcessHead;
20 extern PEPROCESS PsIdleProcess;
21 extern PVOID PspSystemDllEntryPoint;
22 extern PHANDLE_TABLE PspCidTable;
23
24 POBJECT_TYPE PsThreadType = NULL;
25
26 /* FUNCTIONS ***************************************************************/
27
28 VOID
29 STDCALL
30 PspThreadSpecialApc(PKAPC Apc,
31 PKNORMAL_ROUTINE* NormalRoutine,
32 PVOID* NormalContext,
33 PVOID* SystemArgument1,
34 PVOID* SystemArgument2)
35 {
36 ExFreePool(Apc);
37 }
38
39 VOID
40 STDCALL
41 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine,
42 PVOID StartContext)
43 {
44 PKAPC ThreadApc;
45 PETHREAD Thread = PsGetCurrentThread();
46
47 DPRINT("I am a new USER thread. This is my start routine: %p. This my context: %p."
48 "This is my IRQL: %d. This is my Thread Pointer: %x.\n", StartRoutine,
49 StartContext, KeGetCurrentIrql(), Thread);
50
51 if (!Thread->Terminated) {
52
53 /* Allocate the APC */
54 ThreadApc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('T', 'h', 'r','d'));
55
56 /* Initialize it */
57 KeInitializeApc(ThreadApc,
58 &Thread->Tcb,
59 OriginalApcEnvironment,
60 PspThreadSpecialApc,
61 NULL,
62 PspSystemDllEntryPoint,
63 UserMode,
64 NULL);
65
66 /* Insert it into the queue */
67 KeInsertQueueApc(ThreadApc, NULL, NULL, IO_NO_INCREMENT);
68 Thread->Tcb.ApcState.UserApcPending = TRUE;
69 }
70
71 /* Go to Passive Level and notify debugger */
72 KeLowerIrql(PASSIVE_LEVEL);
73 DbgkCreateThread(StartContext);
74 }
75
76 VOID
77 STDCALL
78 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine,
79 PVOID StartContext)
80 {
81 PETHREAD Thread = PsGetCurrentThread();
82
83 /* Unlock the dispatcher Database */
84 KeLowerIrql(PASSIVE_LEVEL);
85
86 /* Make sure it's not terminated by now */
87 if (!Thread->Terminated) {
88
89 /* Call it */
90 (StartRoutine)(StartContext);
91 }
92
93 /* Exit the thread */
94 PspExitThread(STATUS_SUCCESS);
95 }
96
97 NTSTATUS
98 STDCALL
99 PspCreateThread(OUT PHANDLE ThreadHandle,
100 IN ACCESS_MASK DesiredAccess,
101 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
102 IN HANDLE ProcessHandle,
103 IN PEPROCESS TargetProcess,
104 OUT PCLIENT_ID ClientId,
105 IN PCONTEXT ThreadContext,
106 IN PINITIAL_TEB InitialTeb,
107 IN BOOLEAN CreateSuspended,
108 IN PKSTART_ROUTINE StartRoutine OPTIONAL,
109 IN PVOID StartContext OPTIONAL)
110 {
111 HANDLE hThread;
112 PEPROCESS Process;
113 PETHREAD Thread;
114 PTEB TebBase;
115 KIRQL OldIrql;
116 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
117 NTSTATUS Status;
118 HANDLE_TABLE_ENTRY CidEntry;
119 ULONG_PTR KernelStack;
120
121 /* Reference the Process by handle or pointer, depending on what we got */
122 DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle, TargetProcess, ThreadContext);
123 if (ProcessHandle) {
124
125 /* Normal thread or System Thread */
126 DPRINT("Referencing Parent Process\n");
127 Status = ObReferenceObjectByHandle(ProcessHandle,
128 PROCESS_CREATE_THREAD,
129 PsProcessType,
130 PreviousMode,
131 (PVOID*)&Process,
132 NULL);
133 } else {
134
135 /* System thread inside System Process, or Normal Thread with a bug */
136 if (StartRoutine) {
137
138 /* Reference the Process by Pointer */
139 DPRINT("Referencing Parent System Process\n");
140 ObReferenceObject(TargetProcess);
141 Process = TargetProcess;
142 Status = STATUS_SUCCESS;
143
144 } else {
145
146 /* Fake ObReference returning this */
147 Status = STATUS_INVALID_HANDLE;
148 }
149 }
150
151 /* Check for success */
152 if(!NT_SUCCESS(Status)) {
153
154 DPRINT1("Invalid Process Handle, or no handle given\n");
155 return(Status);
156 }
157
158 /* Create Thread Object */
159 DPRINT("Creating Thread Object\n");
160 Status = ObCreateObject(PreviousMode,
161 PsThreadType,
162 ObjectAttributes,
163 KernelMode,
164 NULL,
165 sizeof(ETHREAD),
166 0,
167 0,
168 (PVOID*)&Thread);
169
170 /* Check for success */
171 if (!NT_SUCCESS(Status)) {
172
173 /* Dereference the Process */
174 DPRINT1("Failed to Create Thread Object\n");
175 ObDereferenceObject(Process);
176 return(Status);
177 }
178
179 /* Zero the Object entirely */
180 DPRINT("Cleaning Thread Object\n");
181 RtlZeroMemory(Thread, sizeof(ETHREAD));
182
183 /* Create Cid Handle */
184 DPRINT("Creating Thread Handle (CID)\n");
185 CidEntry.Object = Thread;
186 CidEntry.GrantedAccess = 0;
187 Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
188 if (!Thread->Cid.UniqueThread) {
189
190 DPRINT1("Failed to create Thread Handle (CID)\n");
191 ObDereferenceObject(Process);
192 ObDereferenceObject(Thread);
193 return STATUS_INSUFFICIENT_RESOURCES;
194 }
195
196 /* Initialize Lists */
197 DPRINT("Initialliazing Thread Lists and Locks\n");
198 InitializeListHead(&Thread->LpcReplyChain);
199 InitializeListHead(&Thread->IrpList);
200 InitializeListHead(&Thread->ActiveTimerListHead);
201 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
202
203 /* Initialize LPC */
204 DPRINT("Initialliazing Thread Semaphore\n");
205 KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, MAXLONG);
206
207 /* Allocate Stack for non-GUI Thread */
208 DPRINT("Initialliazing Thread Stack\n");
209 KernelStack = (ULONG_PTR)MmCreateKernelStack(FALSE) + KERNEL_STACK_SIZE;
210
211 /* Set the Process CID */
212 DPRINT("Initialliazing Thread PID and Parent Process\n");
213 Thread->Cid.UniqueProcess = Process->UniqueProcessId;
214 Thread->ThreadsProcess = Process;
215
216 /* Now let the kernel initialize the context */
217 if (ThreadContext) {
218
219 /* User-mode Thread */
220
221 /* Create Teb */
222 DPRINT("Initialliazing Thread PEB\n");
223 TebBase = MmCreateTeb(Process, &Thread->Cid, InitialTeb);
224
225 /* Set the Start Addresses */
226 DPRINT("Initialliazing Thread Start Addresses :%x, %x\n", ThreadContext->Eip, ThreadContext->Eax);
227 Thread->StartAddress = (PVOID)ThreadContext->Eip;
228 Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
229
230 /* Let the kernel intialize the Thread */
231 DPRINT("Initialliazing Kernel Thread\n");
232 KeInitializeThread(&Process->Pcb,
233 &Thread->Tcb,
234 PspUserThreadStartup,
235 NULL,
236 NULL,
237 ThreadContext,
238 TebBase,
239 (PVOID)KernelStack);
240
241 } else {
242
243 /* System Thread */
244 DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine);
245 Thread->StartAddress = StartRoutine;
246 Thread->SystemThread = TRUE;
247
248 /* Let the kernel intialize the Thread */
249 DPRINT("Initialliazing Kernel Thread\n");
250 KeInitializeThread(&Process->Pcb,
251 &Thread->Tcb,
252 PspSystemThreadStartup,
253 StartRoutine,
254 StartContext,
255 NULL,
256 NULL,
257 (PVOID)KernelStack);
258 }
259
260 /*
261 * Insert the Thread into the Process's Thread List
262 * Note, this is the ETHREAD Thread List. It is removed in
263 * ps/kill.c!PspExitThread.
264 */
265 DPRINT("Inserting into Process Thread List \n");
266 InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
267
268 /* Notify Thread Creation */
269 DPRINT("Running Thread Notify \n");
270 PspRunCreateThreadNotifyRoutines(Thread, TRUE);
271
272 /* Suspend the Thread if we have to */
273 if (CreateSuspended) {
274
275 DPRINT("Suspending Thread\n");
276 KeSuspendThread(&Thread->Tcb);
277 }
278
279 /* Reference ourselves as a keep-alive */
280 ObReferenceObject(Thread);
281
282 /* Insert the Thread into the Object Manager */
283 DPRINT("Inserting Thread\n");
284 Status = ObInsertObject((PVOID)Thread,
285 NULL,
286 DesiredAccess,
287 0,
288 NULL,
289 &hThread);
290
291 /* Return Cid and Handle */
292 DPRINT("All worked great!\n");
293 if(NT_SUCCESS(Status)) {
294
295 _SEH_TRY {
296
297 if(ClientId != NULL) {
298
299 *ClientId = Thread->Cid;
300 }
301 *ThreadHandle = hThread;
302
303 } _SEH_HANDLE {
304
305 Status = _SEH_GetExceptionCode();
306
307 } _SEH_END;
308 }
309
310 /* FIXME: SECURITY */
311
312 /* Dispatch thread */
313 DPRINT("About to dispatch the thread: %x!\n", &Thread->Tcb);
314 OldIrql = KeAcquireDispatcherDatabaseLock ();
315 KiUnblockThread(&Thread->Tcb, NULL, 0);
316 ObDereferenceObject(Thread);
317 KeReleaseDispatcherDatabaseLock(OldIrql);
318
319 /* Return */
320 DPRINT("Returning\n");
321 return Status;
322 }
323
324 /*
325 * @implemented
326 */
327 NTSTATUS
328 STDCALL
329 PsCreateSystemThread(PHANDLE ThreadHandle,
330 ACCESS_MASK DesiredAccess,
331 POBJECT_ATTRIBUTES ObjectAttributes,
332 HANDLE ProcessHandle,
333 PCLIENT_ID ClientId,
334 PKSTART_ROUTINE StartRoutine,
335 PVOID StartContext)
336 {
337 PEPROCESS TargetProcess = NULL;
338 HANDLE Handle = ProcessHandle;
339
340 /* Check if we have a handle. If not, use the System Process */
341 if (!ProcessHandle) {
342
343 Handle = NULL;
344 TargetProcess = PsInitialSystemProcess;
345 }
346
347 /* Call the shared function */
348 return PspCreateThread(ThreadHandle,
349 DesiredAccess,
350 ObjectAttributes,
351 Handle,
352 TargetProcess,
353 ClientId,
354 NULL,
355 NULL,
356 FALSE,
357 StartRoutine,
358 StartContext);
359 }
360
361 /*
362 * @implemented
363 */
364 NTSTATUS
365 STDCALL
366 PsLookupThreadByThreadId(IN HANDLE ThreadId,
367 OUT PETHREAD *Thread)
368 {
369 PHANDLE_TABLE_ENTRY CidEntry;
370 PETHREAD FoundThread;
371 NTSTATUS Status = STATUS_INVALID_PARAMETER;
372 PAGED_CODE();
373
374 KeEnterCriticalRegion();
375
376 /* Get the CID Handle Entry */
377 if ((CidEntry = ExMapHandleToPointer(PspCidTable,
378 ThreadId)))
379 {
380 /* Get the Process */
381 FoundThread = CidEntry->Object;
382
383 /* Make sure it's really a process */
384 if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
385 {
386 /* Reference and return it */
387 ObReferenceObject(FoundThread);
388 *Thread = FoundThread;
389 Status = STATUS_SUCCESS;
390 }
391
392 /* Unlock the Entry */
393 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
394 }
395
396 KeLeaveCriticalRegion();
397
398 /* Return to caller */
399 return Status;
400 }
401
402 /*
403 * @implemented
404 */
405 HANDLE
406 STDCALL
407 PsGetCurrentThreadId(VOID)
408 {
409 return(PsGetCurrentThread()->Cid.UniqueThread);
410 }
411
412 /*
413 * @implemented
414 */
415 ULONG
416 STDCALL
417 PsGetThreadFreezeCount(PETHREAD Thread)
418 {
419 return Thread->Tcb.FreezeCount;
420 }
421
422 /*
423 * @implemented
424 */
425 BOOLEAN
426 STDCALL
427 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
428 {
429 return Thread->HardErrorsAreDisabled;
430 }
431
432 /*
433 * @implemented
434 */
435 HANDLE
436 STDCALL
437 PsGetThreadId(PETHREAD Thread)
438 {
439 return Thread->Cid.UniqueThread;
440 }
441
442 /*
443 * @implemented
444 */
445 PEPROCESS
446 STDCALL
447 PsGetThreadProcess(PETHREAD Thread)
448 {
449 return Thread->ThreadsProcess;
450 }
451
452 /*
453 * @implemented
454 */
455 HANDLE
456 STDCALL
457 PsGetThreadProcessId(PETHREAD Thread)
458 {
459 return Thread->Cid.UniqueProcess;
460 }
461
462 /*
463 * @implemented
464 */
465 HANDLE
466 STDCALL
467 PsGetThreadSessionId(PETHREAD Thread)
468 {
469 return (HANDLE)Thread->ThreadsProcess->Session;
470 }
471
472 /*
473 * @implemented
474 */
475 PTEB
476 STDCALL
477 PsGetThreadTeb(PETHREAD Thread)
478 {
479 return Thread->Tcb.Teb;
480 }
481
482 /*
483 * @implemented
484 */
485 PVOID
486 STDCALL
487 PsGetThreadWin32Thread(PETHREAD Thread)
488 {
489 return Thread->Tcb.Win32Thread;
490 }
491
492 /*
493 * @implemented
494 */
495 KPROCESSOR_MODE
496 STDCALL
497 PsGetCurrentThreadPreviousMode(VOID)
498 {
499 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
500 }
501
502 /*
503 * @implemented
504 */
505 PVOID
506 STDCALL
507 PsGetCurrentThreadStackBase(VOID)
508 {
509 return PsGetCurrentThread()->Tcb.StackBase;
510 }
511
512 /*
513 * @implemented
514 */
515 PVOID
516 STDCALL
517 PsGetCurrentThreadStackLimit(VOID)
518 {
519 return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
520 }
521
522 /*
523 * @implemented
524 */
525 BOOLEAN
526 STDCALL
527 PsIsThreadTerminating(IN PETHREAD Thread)
528 {
529 return (Thread->Terminated ? TRUE : FALSE);
530 }
531
532 /*
533 * @implemented
534 */
535 BOOLEAN
536 STDCALL
537 PsIsSystemThread(PETHREAD Thread)
538 {
539 return (Thread->SystemThread ? TRUE: FALSE);
540 }
541
542 /*
543 * @implemented
544 */
545 BOOLEAN
546 STDCALL
547 PsIsThreadImpersonating(PETHREAD Thread)
548 {
549 return Thread->ActiveImpersonationInfo;
550 }
551
552 /*
553 * @implemented
554 */
555 VOID
556 STDCALL
557 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
558 BOOLEAN HardErrorsAreDisabled)
559 {
560 Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
561 }
562
563 /*
564 * @implemented
565 */
566 VOID
567 STDCALL
568 PsSetThreadWin32Thread(PETHREAD Thread,
569 PVOID Win32Thread)
570 {
571 Thread->Tcb.Win32Thread = Win32Thread;
572 }
573
574 NTSTATUS
575 STDCALL
576 NtCreateThread(OUT PHANDLE ThreadHandle,
577 IN ACCESS_MASK DesiredAccess,
578 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
579 IN HANDLE ProcessHandle,
580 OUT PCLIENT_ID ClientId,
581 IN PCONTEXT ThreadContext,
582 IN PINITIAL_TEB InitialTeb,
583 IN BOOLEAN CreateSuspended)
584 {
585 INITIAL_TEB SafeInitialTeb;
586 CONTEXT SafeContext;
587 NTSTATUS Status = STATUS_SUCCESS;
588
589 PAGED_CODE();
590
591 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
592 ThreadHandle,ThreadContext);
593
594 if(KeGetPreviousMode() != KernelMode) {
595
596 if (ThreadContext == NULL) {
597 DPRINT1("No context for User-Mode Thread!!\n");
598 return STATUS_INVALID_PARAMETER;
599 }
600
601 _SEH_TRY {
602
603 ProbeForWriteHandle(ThreadHandle);
604
605 if(ClientId != NULL) {
606
607 ProbeForWrite(ClientId,
608 sizeof(CLIENT_ID),
609 sizeof(ULONG));
610 }
611
612 if(ThreadContext != NULL) {
613
614 ProbeForRead(ThreadContext,
615 sizeof(CONTEXT),
616 sizeof(ULONG));
617 SafeContext = *ThreadContext;
618 ThreadContext = &SafeContext;
619 }
620
621 ProbeForRead(InitialTeb,
622 sizeof(INITIAL_TEB),
623 sizeof(ULONG));
624 SafeInitialTeb = *InitialTeb;
625 InitialTeb = &SafeInitialTeb;
626
627 } _SEH_HANDLE {
628
629 Status = _SEH_GetExceptionCode();
630
631 } _SEH_END;
632
633 if (!NT_SUCCESS(Status)) return Status;
634 }
635
636 /* Call the shared function */
637 return PspCreateThread(ThreadHandle,
638 DesiredAccess,
639 ObjectAttributes,
640 ProcessHandle,
641 NULL,
642 ClientId,
643 ThreadContext,
644 InitialTeb,
645 CreateSuspended,
646 NULL,
647 NULL);
648 }
649
650 /*
651 * @implemented
652 */
653 NTSTATUS
654 STDCALL
655 NtOpenThread(OUT PHANDLE ThreadHandle,
656 IN ACCESS_MASK DesiredAccess,
657 IN POBJECT_ATTRIBUTES ObjectAttributes,
658 IN PCLIENT_ID ClientId OPTIONAL)
659 {
660 KPROCESSOR_MODE PreviousMode;
661 CLIENT_ID SafeClientId;
662 ULONG Attributes = 0;
663 HANDLE hThread = NULL;
664 NTSTATUS Status = STATUS_SUCCESS;
665 PETHREAD Thread;
666 BOOLEAN HasObjectName = FALSE;
667
668 PAGED_CODE();
669
670 PreviousMode = KeGetPreviousMode();
671
672 /* Probe the paraemeters */
673 if(PreviousMode != KernelMode)
674 {
675 _SEH_TRY
676 {
677 ProbeForWriteHandle(ThreadHandle);
678
679 if(ClientId != NULL)
680 {
681 ProbeForRead(ClientId,
682 sizeof(CLIENT_ID),
683 sizeof(ULONG));
684
685 SafeClientId = *ClientId;
686 ClientId = &SafeClientId;
687 }
688
689 /* just probe the object attributes structure, don't capture it
690 completely. This is done later if necessary */
691 ProbeForRead(ObjectAttributes,
692 sizeof(OBJECT_ATTRIBUTES),
693 sizeof(ULONG));
694 HasObjectName = (ObjectAttributes->ObjectName != NULL);
695 Attributes = ObjectAttributes->Attributes;
696 }
697 _SEH_HANDLE
698 {
699 Status = _SEH_GetExceptionCode();
700 }
701 _SEH_END;
702
703 if(!NT_SUCCESS(Status)) return Status;
704 }
705 else
706 {
707 HasObjectName = (ObjectAttributes->ObjectName != NULL);
708 Attributes = ObjectAttributes->Attributes;
709 }
710
711 if (HasObjectName && ClientId != NULL)
712 {
713 /* can't pass both, n object name and a client id */
714 return STATUS_INVALID_PARAMETER_MIX;
715 }
716
717 /* Open by name if one was given */
718 if (HasObjectName)
719 {
720 /* Open it */
721 Status = ObOpenObjectByName(ObjectAttributes,
722 PsThreadType,
723 PreviousMode,
724 NULL,
725 DesiredAccess,
726 NULL,
727 &hThread);
728
729 if (!NT_SUCCESS(Status))
730 {
731 DPRINT1("Could not open object by name\n");
732 }
733 }
734 else if (ClientId != NULL)
735 {
736 /* Open by Thread ID */
737 if (ClientId->UniqueProcess)
738 {
739 /* Get the Process */
740 DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess);
741 Status = PsLookupProcessThreadByCid(ClientId,
742 NULL,
743 &Thread);
744 }
745 else
746 {
747 /* Get the Process */
748 DPRINT("Opening by Thread ID: %x\n", ClientId->UniqueThread);
749 Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
750 &Thread);
751 }
752
753 if(!NT_SUCCESS(Status))
754 {
755 DPRINT1("Failure to find Thread\n");
756 return Status;
757 }
758
759 /* Open the Thread Object */
760 Status = ObOpenObjectByPointer(Thread,
761 Attributes,
762 NULL,
763 DesiredAccess,
764 PsThreadType,
765 PreviousMode,
766 &hThread);
767 if(!NT_SUCCESS(Status))
768 {
769 DPRINT1("Failure to open Thread\n");
770 }
771
772 /* Dereference the thread */
773 ObDereferenceObject(Thread);
774 }
775 else
776 {
777 /* neither an object name nor a client id was passed */
778 return STATUS_INVALID_PARAMETER_MIX;
779 }
780
781 /* Write back the handle */
782 if(NT_SUCCESS(Status))
783 {
784 _SEH_TRY
785 {
786 *ThreadHandle = hThread;
787 }
788 _SEH_HANDLE
789 {
790 Status = _SEH_GetExceptionCode();
791 }
792 _SEH_END;
793 }
794
795 /* Return status */
796 return Status;
797 }
798
799 NTSTATUS
800 STDCALL
801 NtYieldExecution(VOID)
802 {
803 KiDispatchThread(Ready);
804 return(STATUS_SUCCESS);
805 }
806
807 NTSTATUS
808 STDCALL
809 NtTestAlert(VOID)
810 {
811 /* Check and Alert Thread if needed */
812 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS;
813 }
814
815 /*
816 * @implemented
817 */
818 KPROCESSOR_MODE
819 STDCALL
820 ExGetPreviousMode (VOID)
821 {
822 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
823 }
824
825 /* EOF */