242bda78596725fe4314909029bcddbbee23e725
[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 KeEnterCriticalRegion();
379
380 /* Get the CID Handle Entry */
381 if ((CidEntry = ExMapHandleToPointer(PspCidTable,
382 ThreadId)))
383 {
384 /* Get the Process */
385 FoundThread = CidEntry->u1.Object;
386
387 /* Make sure it's really a process */
388 if (FoundThread->Tcb.DispatcherHeader.Type == ThreadObject)
389 {
390 /* Reference and return it */
391 ObReferenceObject(FoundThread);
392 *Thread = FoundThread;
393 Status = STATUS_SUCCESS;
394 }
395
396 /* Unlock the Entry */
397 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
398 }
399
400 KeLeaveCriticalRegion();
401
402 /* Return to caller */
403 return Status;
404 }
405
406 /*
407 * @implemented
408 */
409 HANDLE
410 STDCALL
411 PsGetCurrentThreadId(VOID)
412 {
413 return(PsGetCurrentThread()->Cid.UniqueThread);
414 }
415
416 /*
417 * @implemented
418 */
419 ULONG
420 STDCALL
421 PsGetThreadFreezeCount(PETHREAD Thread)
422 {
423 return Thread->Tcb.FreezeCount;
424 }
425
426 /*
427 * @implemented
428 */
429 BOOLEAN
430 STDCALL
431 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread)
432 {
433 return Thread->HardErrorsAreDisabled;
434 }
435
436 /*
437 * @implemented
438 */
439 HANDLE
440 STDCALL
441 PsGetThreadId(PETHREAD Thread)
442 {
443 return Thread->Cid.UniqueThread;
444 }
445
446 /*
447 * @implemented
448 */
449 PEPROCESS
450 STDCALL
451 PsGetThreadProcess(PETHREAD Thread)
452 {
453 return Thread->ThreadsProcess;
454 }
455
456 /*
457 * @implemented
458 */
459 HANDLE
460 STDCALL
461 PsGetThreadProcessId(PETHREAD Thread)
462 {
463 return Thread->Cid.UniqueProcess;
464 }
465
466 /*
467 * @implemented
468 */
469 HANDLE
470 STDCALL
471 PsGetThreadSessionId(PETHREAD Thread)
472 {
473 return (HANDLE)Thread->ThreadsProcess->Session;
474 }
475
476 /*
477 * @implemented
478 */
479 PTEB
480 STDCALL
481 PsGetThreadTeb(PETHREAD Thread)
482 {
483 return Thread->Tcb.Teb;
484 }
485
486 /*
487 * @implemented
488 */
489 PVOID
490 STDCALL
491 PsGetThreadWin32Thread(PETHREAD Thread)
492 {
493 return Thread->Tcb.Win32Thread;
494 }
495
496 /*
497 * @implemented
498 */
499 KPROCESSOR_MODE
500 STDCALL
501 PsGetCurrentThreadPreviousMode(VOID)
502 {
503 return (KPROCESSOR_MODE)PsGetCurrentThread()->Tcb.PreviousMode;
504 }
505
506 /*
507 * @implemented
508 */
509 PVOID
510 STDCALL
511 PsGetCurrentThreadStackBase(VOID)
512 {
513 return PsGetCurrentThread()->Tcb.StackBase;
514 }
515
516 /*
517 * @implemented
518 */
519 PVOID
520 STDCALL
521 PsGetCurrentThreadStackLimit(VOID)
522 {
523 return (PVOID)PsGetCurrentThread()->Tcb.StackLimit;
524 }
525
526 /*
527 * @implemented
528 */
529 BOOLEAN
530 STDCALL
531 PsIsThreadTerminating(IN PETHREAD Thread)
532 {
533 return (Thread->Terminated ? TRUE : FALSE);
534 }
535
536 /*
537 * @implemented
538 */
539 BOOLEAN
540 STDCALL
541 PsIsSystemThread(PETHREAD Thread)
542 {
543 return (Thread->SystemThread ? TRUE: FALSE);
544 }
545
546 /*
547 * @implemented
548 */
549 BOOLEAN
550 STDCALL
551 PsIsThreadImpersonating(PETHREAD Thread)
552 {
553 return Thread->ActiveImpersonationInfo;
554 }
555
556 /*
557 * @implemented
558 */
559 VOID
560 STDCALL
561 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread,
562 BOOLEAN HardErrorsAreDisabled)
563 {
564 Thread->HardErrorsAreDisabled = HardErrorsAreDisabled;
565 }
566
567 /*
568 * @implemented
569 */
570 VOID
571 STDCALL
572 PsSetThreadWin32Thread(PETHREAD Thread,
573 PVOID Win32Thread)
574 {
575 Thread->Tcb.Win32Thread = Win32Thread;
576 }
577
578 NTSTATUS
579 STDCALL
580 NtCreateThread(OUT PHANDLE ThreadHandle,
581 IN ACCESS_MASK DesiredAccess,
582 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
583 IN HANDLE ProcessHandle,
584 OUT PCLIENT_ID ClientId,
585 IN PCONTEXT ThreadContext,
586 IN PINITIAL_TEB InitialTeb,
587 IN BOOLEAN CreateSuspended)
588 {
589 INITIAL_TEB SafeInitialTeb;
590
591 PAGED_CODE();
592
593 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
594 ThreadHandle,ThreadContext);
595
596 if(KeGetPreviousMode() != KernelMode) {
597
598 _SEH_TRY {
599
600 ProbeForWriteHandle(ThreadHandle);
601
602 if(ClientId != NULL) {
603
604 ProbeForWrite(ClientId,
605 sizeof(CLIENT_ID),
606 sizeof(ULONG));
607 }
608
609 if(ThreadContext != NULL) {
610
611 ProbeForRead(ThreadContext,
612 sizeof(CONTEXT),
613 sizeof(ULONG));
614
615 } else {
616
617 DPRINT1("No context for User-Mode Thread!!\n");
618 return STATUS_INVALID_PARAMETER;
619 }
620
621 ProbeForRead(InitialTeb,
622 sizeof(INITIAL_TEB),
623 sizeof(ULONG));
624
625 } _SEH_HANDLE {
626
627 return _SEH_GetExceptionCode();
628
629 } _SEH_END;
630 }
631
632 /* Use probed data for the Initial TEB */
633 SafeInitialTeb = *InitialTeb; /* FIXME - not protected! */
634 InitialTeb = &SafeInitialTeb;
635
636 /* Call the shared function */
637 return PspCreateThread(ThreadHandle, /* FIXME - not protected! */
638 DesiredAccess,
639 ObjectAttributes,
640 ProcessHandle,
641 NULL,
642 ClientId, /* FIXME - not protected! */
643 ThreadContext, /* FIXME - not protected! */
644 InitialTeb, /* FIXME - not protected! */
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 OPTIONAL,
658 IN PCLIENT_ID ClientId OPTIONAL)
659 {
660 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
661 CLIENT_ID SafeClientId;
662 HANDLE hThread = 0;
663 NTSTATUS Status = STATUS_SUCCESS;
664 PETHREAD Thread;
665
666 PAGED_CODE();
667
668 /* Probe the paraemeters */
669 if(PreviousMode != KernelMode)
670 {
671 _SEH_TRY
672 {
673 ProbeForWriteHandle(ThreadHandle);
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) /* FIXME - neither probed nor protected! */
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 /* FIXME - would be a good idea to return the handle in case of success! */
711 /* Return Status */
712 return(Status);
713 }
714 else if (ClientId)
715 {
716 /* Open by Thread ID */
717 if (ClientId->UniqueProcess) /* FIXME - neither probed nor protected! */
718 {
719 /* Get the Process */
720 DPRINT("Opening by Process ID: %x\n", ClientId->UniqueProcess); /* FIXME - neither probed nor protected! */
721 Status = PsLookupProcessThreadByCid(ClientId, /* FIXME - neither probed nor protected! */
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, /* FIXME - neither probed nor protected! */
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 */