3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/se.h>
28 #include <internal/id.h>
29 #include <internal/dbg.h>
30 #include <internal/ldr.h>
33 #include <internal/debug.h>
35 /* GLOBAL *******************************************************************/
37 static ULONG PiNextThreadUniqueId = 0;
39 extern KSPIN_LOCK PiThreadListLock;
40 extern ULONG PiNrThreads;
42 extern LIST_ENTRY PiThreadListHead;
45 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
47 static ULONG PiThreadNotifyRoutineCount = 0;
48 static PCREATE_THREAD_NOTIFY_ROUTINE
49 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
50 #endif /* LIBCAPTIVE */
52 /* FUNCTIONS ***************************************************************/
57 PsAssignImpersonationToken(PETHREAD Thread,
61 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
64 if (TokenHandle != NULL)
66 Status = ObReferenceObjectByHandle(TokenHandle,
72 if (!NT_SUCCESS(Status))
76 ImpersonationLevel = Token->ImpersonationLevel;
81 ImpersonationLevel = 0;
84 PsImpersonateClient(Thread,
91 ObDereferenceObject(Token);
93 return(STATUS_SUCCESS);
101 Thread = PsGetCurrentThread();
103 if (Thread->ActiveImpersonationInfo != 0)
105 ObDereferenceObject(Thread->ImpersonationInfo->Token);
106 Thread->ActiveImpersonationInfo = 0;
111 PsImpersonateClient(PETHREAD Thread,
115 SECURITY_IMPERSONATION_LEVEL Level)
119 if (Thread->ActiveImpersonationInfo != 0)
121 Thread->ActiveImpersonationInfo = 0;
122 if (Thread->ImpersonationInfo->Token != NULL)
124 ObDereferenceObject(Thread->ImpersonationInfo->Token);
129 if (Thread->ActiveImpersonationInfo == 0 ||
130 Thread->ImpersonationInfo == NULL)
132 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
133 sizeof(PS_IMPERSONATION_INFO));
135 Thread->ImpersonationInfo->Level = Level;
136 Thread->ImpersonationInfo->Unknown2 = c;
137 Thread->ImpersonationInfo->Unknown1 = b;
138 Thread->ImpersonationInfo->Token = Token;
139 ObReferenceObjectByPointer(Token,
143 Thread->ActiveImpersonationInfo = 1;
147 PsReferenceEffectiveToken(PETHREAD Thread,
148 PTOKEN_TYPE TokenType,
150 PSECURITY_IMPERSONATION_LEVEL Level)
155 if (Thread->ActiveImpersonationInfo == 0)
157 Process = Thread->ThreadsProcess;
158 *TokenType = TokenPrimary;
160 Token = Process->Token;
164 Token = Thread->ImpersonationInfo->Token;
165 *TokenType = TokenImpersonation;
166 *b = Thread->ImpersonationInfo->Unknown2;
167 *Level = Thread->ImpersonationInfo->Level;
173 NtImpersonateThread(IN HANDLE ThreadHandle,
174 IN HANDLE ThreadToImpersonateHandle,
175 IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService)
178 PETHREAD ThreadToImpersonate;
180 SECURITY_CLIENT_CONTEXT ClientContext;
182 Status = ObReferenceObjectByHandle(ThreadHandle,
188 if (!NT_SUCCESS(Status))
193 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
197 (PVOID*)&ThreadToImpersonate,
199 if (!NT_SUCCESS(Status))
201 ObDereferenceObject(Thread);
205 Status = SeCreateClientSecurity(ThreadToImpersonate,
206 SecurityQualityOfService,
209 if (!NT_SUCCESS(Status))
211 ObDereferenceObject(Thread);
212 ObDereferenceObject(ThreadToImpersonate);
216 SeImpersonateClient(&ClientContext, Thread);
217 if (ClientContext.Token != NULL)
219 ObDereferenceObject(ClientContext.Token);
221 return(STATUS_SUCCESS);
225 NtOpenThreadToken(IN HANDLE ThreadHandle,
226 IN ACCESS_MASK DesiredAccess,
227 IN BOOLEAN OpenAsSelf,
228 OUT PHANDLE TokenHandle)
235 Status = ObReferenceObjectByHandle(ThreadHandle,
241 if (!NT_SUCCESS(Status))
246 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
247 SepCreateImpersonationTokenDacl(Token);
249 return(STATUS_UNSUCCESSFUL);
252 #endif /* LIBCAPTIVE */
254 PACCESS_TOKEN STDCALL
255 PsReferenceImpersonationToken(PETHREAD Thread,
258 SECURITY_IMPERSONATION_LEVEL* Level)
260 if (Thread->ActiveImpersonationInfo == 0)
269 #else /* !LIBCAPTIVE */
270 *Level = Thread->ImpersonationInfo->Level;
271 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
272 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
273 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
277 return(Thread->ImpersonationInfo->Token);
278 #endif /* LIBCAPTIVE */
284 PiBeforeBeginThread(CONTEXT c)
286 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
287 KeLowerIrql(PASSIVE_LEVEL);
291 PiDeleteThread(PVOID ObjectBody)
296 Thread = (PETHREAD)ObjectBody;
298 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
300 ObDereferenceObject(Thread->ThreadsProcess);
301 Thread->ThreadsProcess = NULL;
303 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
305 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
307 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
308 Thread->Cid.UniqueThread,
312 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
313 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
314 KeReleaseThread(Thread);
315 DPRINT("PiDeleteThread() finished\n");
318 #endif /* LIBCAPTIVE */
321 PsInitializeThread(HANDLE ProcessHandle,
323 PHANDLE ThreadHandle,
324 ACCESS_MASK DesiredAccess,
325 POBJECT_ATTRIBUTES ThreadAttributes,
334 #endif /* LIBCAPTIVE */
339 if (ProcessHandle != NULL)
341 Status = ObReferenceObjectByHandle(ProcessHandle,
342 PROCESS_CREATE_THREAD,
347 if (Status != STATUS_SUCCESS)
349 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
352 DPRINT( "Creating thread in process %x\n", Process );
356 Process = PsInitialSystemProcess;
357 ObReferenceObjectByPointer(Process,
358 PROCESS_CREATE_THREAD,
364 * Create and initialize thread
366 Status = ObCreateObject(ThreadHandle,
371 if (!NT_SUCCESS(Status))
376 DPRINT("Thread = %x\n",Thread);
380 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
381 Thread->ThreadsProcess = Process;
383 * FIXME: What lock protects this?
385 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
386 &Thread->Tcb.ProcessThreadListEntry);
387 InitializeListHead(&Thread->TerminationPortList);
388 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
389 InitializeListHead(&Thread->IrpList);
390 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
391 &PiNextThreadUniqueId);
392 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
393 Thread->DeadThread = 0;
394 Thread->Win32Thread = 0;
395 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
399 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
400 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
401 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
403 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
404 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
407 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
409 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
410 Thread->Cid.UniqueThread,
413 #endif /* LIBCAPTIVE */
415 return(STATUS_SUCCESS);
421 PsCreateTeb(HANDLE ProcessHandle,
424 PINITIAL_TEB InitialTeb)
426 MEMORY_BASIC_INFORMATION Info;
435 TebBase = (PVOID)0x7FFDE000;
440 Status = NtQueryVirtualMemory(ProcessHandle,
442 MemoryBasicInformation,
446 if (!NT_SUCCESS(Status))
448 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
451 /* FIXME: Race between this and the above check */
452 if (Info.State == MEM_FREE)
454 /* The TEB must reside in user space */
455 Status = NtAllocateVirtualMemory(ProcessHandle,
459 MEM_RESERVE | MEM_COMMIT,
461 if (NT_SUCCESS(Status))
467 TebBase = TebBase - TebSize;
470 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
472 /* set all pointers to and from the TEB */
473 Teb.Tib.Self = TebBase;
474 if (Thread->ThreadsProcess)
476 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
478 DPRINT("Teb.Peb %x\n", Teb.Peb);
480 /* store stack information from InitialTeb */
481 if (InitialTeb != NULL)
483 Teb.Tib.StackBase = InitialTeb->StackBase;
484 Teb.Tib.StackLimit = InitialTeb->StackLimit;
485 Teb.DeallocationStack = InitialTeb->StackAllocate;
488 /* more initialization */
489 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
490 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
491 Teb.CurrentLocale = PsDefaultThreadLocaleId;
493 /* Terminate the exception handler list */
494 Teb.Tib.ExceptionList = (PVOID)-1;
496 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
498 /* write TEB data into teb page */
499 Status = NtWriteVirtualMemory(ProcessHandle,
505 if (!NT_SUCCESS(Status))
508 DPRINT1 ("Writing TEB failed!\n");
511 NtFreeVirtualMemory(ProcessHandle,
521 *TebPtr = (PTEB)TebBase;
524 DPRINT("TEB allocated at %p\n", TebBase);
530 LdrInitApcRundownRoutine(PKAPC Apc)
536 LdrInitApcKernelRoutine(PKAPC Apc,
537 PKNORMAL_ROUTINE* NormalRoutine,
538 PVOID* NormalContext,
539 PVOID* SystemArgument1,
540 PVOID* SystemArgument2)
546 NtCreateThread(PHANDLE ThreadHandle,
547 ACCESS_MASK DesiredAccess,
548 POBJECT_ATTRIBUTES ObjectAttributes,
549 HANDLE ProcessHandle,
551 PCONTEXT ThreadContext,
552 PINITIAL_TEB InitialTeb,
553 BOOLEAN CreateSuspended)
560 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
561 ThreadHandle,ThreadContext);
563 Status = PsInitializeThread(ProcessHandle,
569 if (!NT_SUCCESS(Status))
574 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
575 if (!NT_SUCCESS(Status))
580 Status = PsCreateTeb(ProcessHandle,
584 if (!NT_SUCCESS(Status))
588 Thread->Tcb.Teb = TebBase;
590 Thread->StartAddress = NULL;
594 *Client = Thread->Cid;
598 * Maybe send a message to the process's debugger
600 DbgkCreateThread((PVOID)ThreadContext->Eip);
603 * First, force the thread to be non-alertable for user-mode alerts.
605 Thread->Tcb.Alertable = FALSE;
608 * If the thread is to be created suspended then queue an APC to
609 * do the suspend before we run any userspace code.
613 PsSuspendThread(Thread, NULL);
617 * Queue an APC to the thread that will execute the ntdll startup
620 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
621 KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
622 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
624 KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
627 * Start the thread running and force it to execute the APC(s) we just
628 * queued before it runs anything else in user-mode.
630 Thread->Tcb.Alertable = TRUE;
631 Thread->Tcb.Alerted[0] = 1;
632 PsUnblockThread(Thread, NULL);
634 return(STATUS_SUCCESS);
639 PsCreateSystemThread(PHANDLE ThreadHandle,
640 ACCESS_MASK DesiredAccess,
641 POBJECT_ATTRIBUTES ObjectAttributes,
642 HANDLE ProcessHandle,
644 PKSTART_ROUTINE StartRoutine,
647 * FUNCTION: Creates a thread which executes in kernel mode
649 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
651 * DesiredAccess = Requested access to the thread
652 * ObjectAttributes = Object attributes (optional)
653 * ProcessHandle = Handle of process thread will run in
654 * NULL to use system process
655 * ClientId (OUT) = Caller supplied storage for the returned client id
656 * of the thread (optional)
657 * StartRoutine = Entry point for the thread
658 * StartContext = Argument supplied to the thread when it begins
660 * RETURNS: Success or failure status
666 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
667 ThreadHandle,ProcessHandle);
669 Status = PsInitializeThread(ProcessHandle,
675 if (!NT_SUCCESS(Status))
680 Thread->StartAddress = StartRoutine;
681 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
682 if (!NT_SUCCESS(Status))
687 if (ClientId != NULL)
689 *ClientId=Thread->Cid;
692 PsUnblockThread(Thread, NULL);
694 return(STATUS_SUCCESS);
699 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
701 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
702 return(STATUS_INSUFFICIENT_RESOURCES);
704 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
705 PiThreadNotifyRoutineCount++;
707 return(STATUS_SUCCESS);
710 #endif /* LIBCAPTIVE */