2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/thread.c
5 * PURPOSE: Thread managment
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 /* INCLUDES ****************************************************************/
15 #include <internal/debug.h>
17 /* GLOBALS ******************************************************************/
19 extern LIST_ENTRY PsActiveProcessHead
;
20 extern PEPROCESS PsIdleProcess
;
21 extern PVOID PspSystemDllEntryPoint
;
22 extern PHANDLE_TABLE PspCidTable
;
24 POBJECT_TYPE PsThreadType
= NULL
;
26 /* FUNCTIONS ***************************************************************/
30 PspThreadSpecialApc(PKAPC Apc
,
31 PKNORMAL_ROUTINE
* NormalRoutine
,
33 PVOID
* SystemArgument1
,
34 PVOID
* SystemArgument2
)
41 PspUserThreadStartup(PKSTART_ROUTINE StartRoutine
,
45 PETHREAD Thread
= PsGetCurrentThread();
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
);
51 if (!Thread
->Terminated
) {
53 /* Allocate the APC */
54 ThreadApc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG('T', 'h', 'r','d'));
57 KeInitializeApc(ThreadApc
,
59 OriginalApcEnvironment
,
62 PspSystemDllEntryPoint
,
66 /* Insert it into the queue */
67 KeInsertQueueApc(ThreadApc
, NULL
, NULL
, IO_NO_INCREMENT
);
68 Thread
->Tcb
.ApcState
.UserApcPending
= TRUE
;
71 /* Go to Passive Level and notify debugger */
72 KeLowerIrql(PASSIVE_LEVEL
);
73 DbgkCreateThread(StartContext
);
78 PspSystemThreadStartup(PKSTART_ROUTINE StartRoutine
,
81 PETHREAD Thread
= PsGetCurrentThread();
83 /* Unlock the dispatcher Database */
84 KeLowerIrql(PASSIVE_LEVEL
);
86 /* Make sure it's not terminated by now */
87 if (!Thread
->Terminated
) {
90 (StartRoutine
)(StartContext
);
94 PspExitThread(STATUS_SUCCESS
);
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
)
116 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
118 HANDLE_TABLE_ENTRY CidEntry
;
119 ULONG_PTR KernelStack
;
121 /* Reference the Process by handle or pointer, depending on what we got */
122 DPRINT("PspCreateThread: %x, %x, %x\n", ProcessHandle
, TargetProcess
, ThreadContext
);
125 /* Normal thread or System Thread */
126 DPRINT("Referencing Parent Process\n");
127 Status
= ObReferenceObjectByHandle(ProcessHandle
,
128 PROCESS_CREATE_THREAD
,
135 /* System thread inside System Process, or Normal Thread with a bug */
138 /* Reference the Process by Pointer */
139 DPRINT("Referencing Parent System Process\n");
140 ObReferenceObject(TargetProcess
);
141 Process
= TargetProcess
;
142 Status
= STATUS_SUCCESS
;
146 /* Fake ObReference returning this */
147 Status
= STATUS_INVALID_HANDLE
;
151 /* Check for success */
152 if(!NT_SUCCESS(Status
)) {
154 DPRINT1("Invalid Process Handle, or no handle given\n");
158 /* Create Thread Object */
159 DPRINT("Creating Thread Object\n");
160 Status
= ObCreateObject(PreviousMode
,
170 /* Check for success */
171 if (!NT_SUCCESS(Status
)) {
173 /* Dereference the Process */
174 DPRINT1("Failed to Create Thread Object\n");
175 ObDereferenceObject(Process
);
179 /* Zero the Object entirely */
180 DPRINT("Cleaning Thread Object\n");
181 RtlZeroMemory(Thread
, sizeof(ETHREAD
));
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
) {
190 DPRINT1("Failed to create Thread Handle (CID)\n");
191 ObDereferenceObject(Process
);
192 ObDereferenceObject(Thread
);
193 return STATUS_INSUFFICIENT_RESOURCES
;
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
);
204 DPRINT("Initialliazing Thread Semaphore\n");
205 KeInitializeSemaphore(&Thread
->LpcReplySemaphore
, 0, MAXLONG
);
207 /* Allocate Stack for non-GUI Thread */
208 DPRINT("Initialliazing Thread Stack\n");
209 KernelStack
= (ULONG_PTR
)MmCreateKernelStack(FALSE
) + KERNEL_STACK_SIZE
;
211 /* Set the Process CID */
212 DPRINT("Initialliazing Thread PID and Parent Process\n");
213 Thread
->Cid
.UniqueProcess
= Process
->UniqueProcessId
;
214 Thread
->ThreadsProcess
= Process
;
216 /* Now let the kernel initialize the context */
219 /* User-mode Thread */
222 DPRINT("Initialliazing Thread PEB\n");
223 TebBase
= MmCreateTeb(Process
, &Thread
->Cid
, InitialTeb
);
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
;
230 /* Let the kernel intialize the Thread */
231 DPRINT("Initialliazing Kernel Thread\n");
232 KeInitializeThread(&Process
->Pcb
,
234 PspUserThreadStartup
,
244 DPRINT("Initialliazing Thread Start Address :%x\n", StartRoutine
);
245 Thread
->StartAddress
= StartRoutine
;
246 Thread
->SystemThread
= TRUE
;
248 /* Let the kernel intialize the Thread */
249 DPRINT("Initialliazing Kernel Thread\n");
250 KeInitializeThread(&Process
->Pcb
,
252 PspSystemThreadStartup
,
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.
265 DPRINT("Inserting into Process Thread List \n");
266 InsertTailList(&Process
->ThreadListHead
, &Thread
->ThreadListEntry
);
268 /* Notify Thread Creation */
269 DPRINT("Running Thread Notify \n");
270 PspRunCreateThreadNotifyRoutines(Thread
, TRUE
);
272 /* Suspend the Thread if we have to */
273 if (CreateSuspended
) {
275 DPRINT("Suspending Thread\n");
276 KeSuspendThread(&Thread
->Tcb
);
279 /* Reference ourselves as a keep-alive */
280 ObReferenceObject(Thread
);
282 /* Insert the Thread into the Object Manager */
283 DPRINT("Inserting Thread\n");
284 Status
= ObInsertObject((PVOID
)Thread
,
291 /* Return Cid and Handle */
292 DPRINT("All worked great!\n");
293 if(NT_SUCCESS(Status
)) {
297 if(ClientId
!= NULL
) {
299 *ClientId
= Thread
->Cid
;
301 *ThreadHandle
= hThread
;
305 Status
= _SEH_GetExceptionCode();
310 /* FIXME: SECURITY */
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
);
320 DPRINT("Returning\n");
329 PsCreateSystemThread(PHANDLE ThreadHandle
,
330 ACCESS_MASK DesiredAccess
,
331 POBJECT_ATTRIBUTES ObjectAttributes
,
332 HANDLE ProcessHandle
,
334 PKSTART_ROUTINE StartRoutine
,
337 PEPROCESS TargetProcess
= NULL
;
338 HANDLE Handle
= ProcessHandle
;
340 /* Check if we have a handle. If not, use the System Process */
341 if (!ProcessHandle
) {
344 TargetProcess
= PsInitialSystemProcess
;
347 /* Call the shared function */
348 return PspCreateThread(ThreadHandle
,
366 PsLookupThreadByThreadId(IN HANDLE ThreadId
,
367 OUT PETHREAD
*Thread
)
369 PHANDLE_TABLE_ENTRY CidEntry
;
370 PETHREAD FoundThread
;
371 NTSTATUS Status
= STATUS_INVALID_PARAMETER
;
374 KeEnterCriticalRegion();
376 /* Get the CID Handle Entry */
377 if ((CidEntry
= ExMapHandleToPointer(PspCidTable
,
380 /* Get the Process */
381 FoundThread
= CidEntry
->Object
;
383 /* Make sure it's really a process */
384 if (FoundThread
->Tcb
.DispatcherHeader
.Type
== ThreadObject
)
386 /* Reference and return it */
387 ObReferenceObject(FoundThread
);
388 *Thread
= FoundThread
;
389 Status
= STATUS_SUCCESS
;
392 /* Unlock the Entry */
393 ExUnlockHandleTableEntry(PspCidTable
, CidEntry
);
396 KeLeaveCriticalRegion();
398 /* Return to caller */
407 PsGetCurrentThreadId(VOID
)
409 return(PsGetCurrentThread()->Cid
.UniqueThread
);
417 PsGetThreadFreezeCount(PETHREAD Thread
)
419 return Thread
->Tcb
.FreezeCount
;
427 PsGetThreadHardErrorsAreDisabled(PETHREAD Thread
)
429 return Thread
->HardErrorsAreDisabled
;
437 PsGetThreadId(PETHREAD Thread
)
439 return Thread
->Cid
.UniqueThread
;
447 PsGetThreadProcess(PETHREAD Thread
)
449 return Thread
->ThreadsProcess
;
457 PsGetThreadProcessId(PETHREAD Thread
)
459 return Thread
->Cid
.UniqueProcess
;
467 PsGetThreadSessionId(PETHREAD Thread
)
469 return (HANDLE
)Thread
->ThreadsProcess
->Session
;
477 PsGetThreadTeb(PETHREAD Thread
)
479 return Thread
->Tcb
.Teb
;
487 PsGetThreadWin32Thread(PETHREAD Thread
)
489 return Thread
->Tcb
.Win32Thread
;
497 PsGetCurrentThreadPreviousMode(VOID
)
499 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;
507 PsGetCurrentThreadStackBase(VOID
)
509 return PsGetCurrentThread()->Tcb
.StackBase
;
517 PsGetCurrentThreadStackLimit(VOID
)
519 return (PVOID
)PsGetCurrentThread()->Tcb
.StackLimit
;
527 PsIsThreadTerminating(IN PETHREAD Thread
)
529 return (Thread
->Terminated
? TRUE
: FALSE
);
537 PsIsSystemThread(PETHREAD Thread
)
539 return (Thread
->SystemThread
? TRUE
: FALSE
);
547 PsIsThreadImpersonating(PETHREAD Thread
)
549 return Thread
->ActiveImpersonationInfo
;
557 PsSetThreadHardErrorsAreDisabled(PETHREAD Thread
,
558 BOOLEAN HardErrorsAreDisabled
)
560 Thread
->HardErrorsAreDisabled
= HardErrorsAreDisabled
;
568 PsSetThreadWin32Thread(PETHREAD Thread
,
571 Thread
->Tcb
.Win32Thread
= Win32Thread
;
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
)
585 INITIAL_TEB SafeInitialTeb
;
587 NTSTATUS Status
= STATUS_SUCCESS
;
591 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
592 ThreadHandle
,ThreadContext
);
594 if(KeGetPreviousMode() != KernelMode
) {
596 if (ThreadContext
== NULL
) {
597 DPRINT1("No context for User-Mode Thread!!\n");
598 return STATUS_INVALID_PARAMETER
;
603 ProbeForWriteHandle(ThreadHandle
);
605 if(ClientId
!= NULL
) {
607 ProbeForWrite(ClientId
,
612 if(ThreadContext
!= NULL
) {
614 ProbeForRead(ThreadContext
,
617 SafeContext
= *ThreadContext
;
618 ThreadContext
= &SafeContext
;
621 ProbeForRead(InitialTeb
,
624 SafeInitialTeb
= *InitialTeb
;
625 InitialTeb
= &SafeInitialTeb
;
629 Status
= _SEH_GetExceptionCode();
633 if (!NT_SUCCESS(Status
)) return Status
;
636 /* Call the shared function */
637 return PspCreateThread(ThreadHandle
,
655 NtOpenThread(OUT PHANDLE ThreadHandle
,
656 IN ACCESS_MASK DesiredAccess
,
657 IN POBJECT_ATTRIBUTES ObjectAttributes
,
658 IN PCLIENT_ID ClientId OPTIONAL
)
660 KPROCESSOR_MODE PreviousMode
;
661 CLIENT_ID SafeClientId
;
662 ULONG Attributes
= 0;
663 HANDLE hThread
= NULL
;
664 NTSTATUS Status
= STATUS_SUCCESS
;
666 BOOLEAN HasObjectName
= FALSE
;
670 PreviousMode
= KeGetPreviousMode();
672 /* Probe the paraemeters */
673 if(PreviousMode
!= KernelMode
)
677 ProbeForWriteHandle(ThreadHandle
);
681 ProbeForRead(ClientId
,
685 SafeClientId
= *ClientId
;
686 ClientId
= &SafeClientId
;
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
),
694 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
695 Attributes
= ObjectAttributes
->Attributes
;
699 Status
= _SEH_GetExceptionCode();
703 if(!NT_SUCCESS(Status
)) return Status
;
707 HasObjectName
= (ObjectAttributes
->ObjectName
!= NULL
);
708 Attributes
= ObjectAttributes
->Attributes
;
711 if (HasObjectName
&& ClientId
!= NULL
)
713 /* can't pass both, n object name and a client id */
714 return STATUS_INVALID_PARAMETER_MIX
;
717 /* Open by name if one was given */
721 Status
= ObOpenObjectByName(ObjectAttributes
,
729 if (!NT_SUCCESS(Status
))
731 DPRINT1("Could not open object by name\n");
734 else if (ClientId
!= NULL
)
736 /* Open by Thread ID */
737 if (ClientId
->UniqueProcess
)
739 /* Get the Process */
740 DPRINT("Opening by Process ID: %x\n", ClientId
->UniqueProcess
);
741 Status
= PsLookupProcessThreadByCid(ClientId
,
747 /* Get the Process */
748 DPRINT("Opening by Thread ID: %x\n", ClientId
->UniqueThread
);
749 Status
= PsLookupThreadByThreadId(ClientId
->UniqueThread
,
753 if(!NT_SUCCESS(Status
))
755 DPRINT1("Failure to find Thread\n");
759 /* Open the Thread Object */
760 Status
= ObOpenObjectByPointer(Thread
,
767 if(!NT_SUCCESS(Status
))
769 DPRINT1("Failure to open Thread\n");
772 /* Dereference the thread */
773 ObDereferenceObject(Thread
);
777 /* neither an object name nor a client id was passed */
778 return STATUS_INVALID_PARAMETER_MIX
;
781 /* Write back the handle */
782 if(NT_SUCCESS(Status
))
786 *ThreadHandle
= hThread
;
790 Status
= _SEH_GetExceptionCode();
801 NtYieldExecution(VOID
)
803 KiDispatchThread(Ready
);
804 return(STATUS_SUCCESS
);
811 /* Check and Alert Thread if needed */
812 return KeTestAlertThread(ExGetPreviousMode()) ? STATUS_ALERTED
: STATUS_SUCCESS
;
820 ExGetPreviousMode (VOID
)
822 return (KPROCESSOR_MODE
)PsGetCurrentThread()->Tcb
.PreviousMode
;