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 PACCESS_TOKEN STDCALL
253 PsReferenceImpersonationToken(PETHREAD Thread,
256 SECURITY_IMPERSONATION_LEVEL* Level)
258 if (Thread->ActiveImpersonationInfo == 0)
263 *Level = Thread->ImpersonationInfo->Level;
264 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
265 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
266 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
270 return(Thread->ImpersonationInfo->Token);
274 PiBeforeBeginThread(CONTEXT c)
276 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
277 KeLowerIrql(PASSIVE_LEVEL);
281 PiDeleteThread(PVOID ObjectBody)
286 Thread = (PETHREAD)ObjectBody;
288 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
290 ObDereferenceObject(Thread->ThreadsProcess);
291 Thread->ThreadsProcess = NULL;
293 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
295 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
297 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
298 Thread->Cid.UniqueThread,
302 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
303 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
304 KeReleaseThread(Thread);
305 DPRINT("PiDeleteThread() finished\n");
308 #endif /* LIBCAPTIVE */
311 PsInitializeThread(HANDLE ProcessHandle,
313 PHANDLE ThreadHandle,
314 ACCESS_MASK DesiredAccess,
315 POBJECT_ATTRIBUTES ThreadAttributes,
324 #endif /* LIBCAPTIVE */
329 if (ProcessHandle != NULL)
331 Status = ObReferenceObjectByHandle(ProcessHandle,
332 PROCESS_CREATE_THREAD,
337 if (Status != STATUS_SUCCESS)
339 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
342 DPRINT( "Creating thread in process %x\n", Process );
346 Process = PsInitialSystemProcess;
347 ObReferenceObjectByPointer(Process,
348 PROCESS_CREATE_THREAD,
354 * Create and initialize thread
356 Status = ObCreateObject(ThreadHandle,
361 if (!NT_SUCCESS(Status))
366 DPRINT("Thread = %x\n",Thread);
370 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
371 Thread->ThreadsProcess = Process;
373 * FIXME: What lock protects this?
375 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
376 &Thread->Tcb.ProcessThreadListEntry);
377 InitializeListHead(&Thread->TerminationPortList);
378 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
379 InitializeListHead(&Thread->IrpList);
380 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
381 &PiNextThreadUniqueId);
382 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
383 Thread->DeadThread = 0;
384 Thread->Win32Thread = 0;
385 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
389 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
390 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
391 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
393 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
394 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
397 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
399 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
400 Thread->Cid.UniqueThread,
403 #endif /* LIBCAPTIVE */
405 return(STATUS_SUCCESS);
411 PsCreateTeb(HANDLE ProcessHandle,
414 PINITIAL_TEB InitialTeb)
416 MEMORY_BASIC_INFORMATION Info;
425 TebBase = (PVOID)0x7FFDE000;
430 Status = NtQueryVirtualMemory(ProcessHandle,
432 MemoryBasicInformation,
436 if (!NT_SUCCESS(Status))
438 CPRINT("NtQueryVirtualMemory (Status %x)\n", Status);
441 /* FIXME: Race between this and the above check */
442 if (Info.State == MEM_FREE)
444 /* The TEB must reside in user space */
445 Status = NtAllocateVirtualMemory(ProcessHandle,
449 MEM_RESERVE | MEM_COMMIT,
451 if (NT_SUCCESS(Status))
457 TebBase = TebBase - TebSize;
460 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
462 /* set all pointers to and from the TEB */
463 Teb.Tib.Self = TebBase;
464 if (Thread->ThreadsProcess)
466 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
468 DPRINT("Teb.Peb %x\n", Teb.Peb);
470 /* store stack information from InitialTeb */
471 if (InitialTeb != NULL)
473 Teb.Tib.StackBase = InitialTeb->StackBase;
474 Teb.Tib.StackLimit = InitialTeb->StackLimit;
475 Teb.DeallocationStack = InitialTeb->StackAllocate;
478 /* more initialization */
479 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
480 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
481 Teb.CurrentLocale = PsDefaultThreadLocaleId;
483 /* Terminate the exception handler list */
484 Teb.Tib.ExceptionList = (PVOID)-1;
486 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
488 /* write TEB data into teb page */
489 Status = NtWriteVirtualMemory(ProcessHandle,
495 if (!NT_SUCCESS(Status))
498 DPRINT1 ("Writing TEB failed!\n");
501 NtFreeVirtualMemory(ProcessHandle,
511 *TebPtr = (PTEB)TebBase;
514 DPRINT("TEB allocated at %p\n", TebBase);
520 LdrInitApcRundownRoutine(PKAPC Apc)
526 LdrInitApcKernelRoutine(PKAPC Apc,
527 PKNORMAL_ROUTINE* NormalRoutine,
528 PVOID* NormalContext,
529 PVOID* SystemArgument1,
530 PVOID* SystemArgument2)
536 NtCreateThread(PHANDLE ThreadHandle,
537 ACCESS_MASK DesiredAccess,
538 POBJECT_ATTRIBUTES ObjectAttributes,
539 HANDLE ProcessHandle,
541 PCONTEXT ThreadContext,
542 PINITIAL_TEB InitialTeb,
543 BOOLEAN CreateSuspended)
550 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
551 ThreadHandle,ThreadContext);
553 Status = PsInitializeThread(ProcessHandle,
559 if (!NT_SUCCESS(Status))
564 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
565 if (!NT_SUCCESS(Status))
570 Status = PsCreateTeb(ProcessHandle,
574 if (!NT_SUCCESS(Status))
578 Thread->Tcb.Teb = TebBase;
580 Thread->StartAddress = NULL;
584 *Client = Thread->Cid;
588 * Maybe send a message to the process's debugger
590 DbgkCreateThread((PVOID)ThreadContext->Eip);
593 * First, force the thread to be non-alertable for user-mode alerts.
595 Thread->Tcb.Alertable = FALSE;
598 * If the thread is to be created suspended then queue an APC to
599 * do the suspend before we run any userspace code.
603 PsSuspendThread(Thread, NULL);
607 * Queue an APC to the thread that will execute the ntdll startup
610 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
611 KeInitializeApc(LdrInitApc, &Thread->Tcb, 0, LdrInitApcKernelRoutine,
612 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
614 KeInsertQueueApc(LdrInitApc, NULL, NULL, UserMode);
617 * Start the thread running and force it to execute the APC(s) we just
618 * queued before it runs anything else in user-mode.
620 Thread->Tcb.Alertable = TRUE;
621 Thread->Tcb.Alerted[0] = 1;
622 PsUnblockThread(Thread, NULL);
624 return(STATUS_SUCCESS);
629 PsCreateSystemThread(PHANDLE ThreadHandle,
630 ACCESS_MASK DesiredAccess,
631 POBJECT_ATTRIBUTES ObjectAttributes,
632 HANDLE ProcessHandle,
634 PKSTART_ROUTINE StartRoutine,
637 * FUNCTION: Creates a thread which executes in kernel mode
639 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
641 * DesiredAccess = Requested access to the thread
642 * ObjectAttributes = Object attributes (optional)
643 * ProcessHandle = Handle of process thread will run in
644 * NULL to use system process
645 * ClientId (OUT) = Caller supplied storage for the returned client id
646 * of the thread (optional)
647 * StartRoutine = Entry point for the thread
648 * StartContext = Argument supplied to the thread when it begins
650 * RETURNS: Success or failure status
656 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
657 ThreadHandle,ProcessHandle);
659 Status = PsInitializeThread(ProcessHandle,
665 if (!NT_SUCCESS(Status))
670 Thread->StartAddress = StartRoutine;
671 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
672 if (!NT_SUCCESS(Status))
677 if (ClientId != NULL)
679 *ClientId=Thread->Cid;
682 PsUnblockThread(Thread, NULL);
684 return(STATUS_SUCCESS);
689 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
691 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
692 return(STATUS_INSUFFICIENT_RESOURCES);
694 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
695 PiThreadNotifyRoutineCount++;
697 return(STATUS_SUCCESS);
700 #endif /* LIBCAPTIVE */