45fd5b29ae4ae513a088b607f7b7e08b98394796
[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 EXPORTED 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 PVOID 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.u1.Object = Thread;
186 CidEntry.u2.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 = MmCreateKernelStack(FALSE);
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 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 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 /* FIXME: Use Lock */
273 DPRINT("Apcs Queueable: %d \n", Thread->Tcb.ApcQueueable);
274 Thread->Tcb.ApcQueueable = TRUE;
275
276 /* Suspend the Thread if we have to */
277 if (CreateSuspended) {
278
279 DPRINT("Suspending Thread\n");
280 KeSuspendThread(&Thread->Tcb);
281 }
282
283 /* Reference ourselves as a keep-alive */
284 ObReferenceObject(Thread);
285
286 /* Insert the Thread into the Object Manager */
287 DPRINT("Inserting Thread\n");
288 Status = ObInsertObject((PVOID)Thread,
289 NULL,
290 DesiredAccess,
291 0,
292 NULL,
293 &hThread);
294
295 /* Return Cid and Handle */
296 DPRINT("All worked great!\n");
297 if(NT_SUCCESS(Status)) {
298
299 _SEH_TRY {
300
301 if(ClientId != NULL) {
302
303 *ClientId = Thread->Cid;
304 }
305 *ThreadHandle = hThread;
306
307 } _SEH_HANDLE {
308
309 Status = _SEH_GetExceptionCode();
310
311 } _SEH_END;
312 }
313
314 /* FIXME: SECURITY */
315
316 /* Dispatch thread */
317 DPRINT("About to dispatch the thread: %x!\n", &Thread->Tcb);
318 OldIrql = KeAcquireDispatcherDatabaseLock ();
319 KiUnblockThread(&Thread->Tcb, NULL, 0);
320 ObDereferenceObject(Thread);
321 KeReleaseDispatcherDatabaseLock(OldIrql);
322
323 /* Return */
324 DPRINT("Returning\n");
325 return Status;
326 }
327
328 /*
329 * @implemented
330 */
331 NTSTATUS
332 STDCALL
333 PsCreateSystemThread(PHANDLE ThreadHandle,
334 ACCESS_MASK DesiredAccess,
335 POBJECT_ATTRIBUTES ObjectAttributes,
336 HANDLE ProcessHandle,
337 PCLIENT_ID ClientId,
338 PKSTART_ROUTINE StartRoutine,
339 PVOID StartContext)
340 {
341 PEPROCESS TargetProcess = NULL;
342 HANDLE Handle = ProcessHandle;
343
344 /* Check if we have a handle. If not, use the System Process */
345 if (!ProcessHandle) {
346
347 Handle = NULL;
348 TargetProcess = PsInitialSystemProcess;
349 }
350
351 /* Call the shared function */
352 return PspCreateThread(ThreadHandle,
353 DesiredAccess,
354 ObjectAttributes,
355 Handle,
356 TargetProcess,
357 ClientId,
358 NULL,
359 NULL,
360 FALSE,
361 StartRoutine,
362 StartContext);
363 }
364
365 /*
366 * @implemented
367 */
368 NTSTATUS
369 STDCALL
370 PsLookupThreadByThreadId(IN HANDLE ThreadId,
371 OUT PETHREAD *Thread)
372 {
373 PHANDLE_TABLE_ENTRY CidEntry;
374 PETHREAD FoundThread;
375 NTSTATUS Status = STATUS_INVALID_PARAMETER;
376 PAGED_CODE();
377
378 /* Get the CID Handle Entry */
379 if ((CidEntry = ExMapHandleToPointer(PspCidTable,
380 ThreadId)))
381 {
382 /* Get the Process */
383 FoundThread = CidEntry->u1.Object;
384
385 /* Make sure it's really a process */
386 if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
387 {
388 /* Reference and return it */
389 ObReferenceObject(FoundThread);
390 *Thread = FoundThread;
391 Status = STATUS_SUCCESS;
392 }
393
394 /* Unlock the Entry */
395 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
396 }
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
587 PAGED_CODE();
588
589 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
590 ThreadHandle,ThreadContext);
591
592 if(KeGetPreviousMode() != KernelMode) {
593
594 _SEH_TRY {
595
596 ProbeForWrite(ThreadHandle,
597 sizeof(HANDLE),
598 sizeof(ULONG));
599
600 if(ClientId != NULL) {
601
602 ProbeForWrite(ClientId,
603 sizeof(CLIENT_ID),
604 sizeof(ULONG));
605 }
606
607 if(ThreadContext != NULL) {
608
609 ProbeForRead(ThreadContext,
610 sizeof(CONTEXT),
611 sizeof(ULONG));
612
613 } else {
614
615 DPRINT1("No context for User-Mode Thread!!\n");
616 return STATUS_INVALID_PARAMETER;
617 }
618
619 ProbeForRead(InitialTeb,
620 sizeof(INITIAL_TEB),
621 sizeof(ULONG));
622
623 } _SEH_HANDLE {
624
625 return _SEH_GetExceptionCode();
626
627 } _SEH_END;
628 }
629
630 /* Use probed data for the Initial TEB */
631 SafeInitialTeb = *InitialTeb;
632 InitialTeb = &SafeInitialTeb;
633
634 /* Call the shared function */
635 return PspCreateThread(ThreadHandle,
636 DesiredAccess,
637 ObjectAttributes,
638 ProcessHandle,
639 NULL,
640 ClientId,
641 ThreadContext,
642 InitialTeb,
643 CreateSuspended,
644 NULL,
645 NULL);
646 }
647
648 /*
649 * @implemented
650 */
651 NTSTATUS
652 STDCALL
653 NtOpenThread(OUT PHANDLE ThreadHandle,
654 IN ACCESS_MASK DesiredAccess,
655 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
656 IN PCLIENT_ID ClientId OPTIONAL)
657 {
658 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
659 CLIENT_ID SafeClientId;
660 HANDLE hThread = 0;
661 NTSTATUS Status = STATUS_SUCCESS;
662 PETHREAD Thread;
663
664 PAGED_CODE();
665
666 /* Probe the paraemeters */
667 if(PreviousMode != KernelMode)
668 {
669 _SEH_TRY
670 {
671 ProbeForWrite(ThreadHandle,
672 sizeof(HANDLE),
673 sizeof(ULONG));
674
675 if(ClientId != NULL)
676 {
677 ProbeForRead(ClientId,
678 sizeof(CLIENT_ID),
679 sizeof(ULONG));
680
681 SafeClientId = *ClientId;
682 ClientId = &SafeClientId;
683 }
684 }
685 _SEH_HANDLE
686 {
687 Status = _SEH_GetExceptionCode();
688 }
689 _SEH_END;
690
691 if(!NT_SUCCESS(Status)) return Status;
692 }
693
694 /* Open by name if one was given */
695 if (ObjectAttributes->ObjectName)
696 {
697 /* Open it */
698 Status = ObOpenObjectByName(ObjectAttributes,
699 PsThreadType,
700 NULL,
701 PreviousMode,
702 DesiredAccess,
703 NULL,
704 hThread);
705
706 if (Status != STATUS_SUCCESS)
707 {
708 DPRINT1("Could not open object by name\n");
709 }
710
711 /* Return Status */
712 return(Status);
713 }
714 else if (ClientId)
715 {
716 /* Open by Thread ID */
717 if (ClientId->UniqueProcess)
718 {
719 /* Get the Process */
720 DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess);
721 Status = PsLookupProcessThreadByCid(ClientId,
722 NULL,
723 &Thread);
724 }
725 else
726 {
727 /* Get the Process */
728 DPRINT("Opening by Thread ID: %x\n", ClientId->UniqueThread);
729 Status = PsLookupThreadByThreadId(ClientId->UniqueThread,
730 &Thread);
731 }
732
733 if(!NT_SUCCESS(Status))
734 {
735 DPRINT1("Failure to find Thread\n");
736 return Status;
737 }
738
739 /* Open the Thread Object */
740 Status = ObOpenObjectByPointer(Thread,
741 ObjectAttributes->Attributes,
742 NULL,
743 DesiredAccess,
744 PsThreadType,
745 PreviousMode,
746 hThread);
747 if(!NT_SUCCESS(Status))
748 {
749 DPRINT1("Failure to open Thread\n");
750 }
751
752 /* Dereference the thread */
753 ObDereferenceObject(Thread);
754 }
755
756 /* Write back the handle */
757 if(NT_SUCCESS(Status))
758 {
759 _SEH_TRY
760 {
761 *ThreadHandle = hThread;
762 }
763 _SEH_HANDLE
764 {
765 Status = _SEH_GetExceptionCode();
766 }
767 _SEH_END;
768 }
769
770 /* Return status */
771 return Status;
772 }
773
774 NTSTATUS
775 STDCALL
776 NtYieldExecution(VOID)
777 {
778 KiDispatchThread(Ready);
779 return(STATUS_SUCCESS);
780 }
781
782 NTSTATUS
783 STDCALL
784 NtTestAlert(VOID)
785 {
786 /* Check and Alert Thread if needed */
787 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED : STATUS_SUCCESS;
788 }
789
790 /*
791 * @implemented
792 */
793 KPROCESSOR_MODE
794 STDCALL
795 ExGetPreviousMode (VOID)
796 {
797 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
798 }
799
800 /* EOF */