b0b7d24c004ceb1847f942e107d9ce4dac0be1c0
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/create.c
6 * PURPOSE: Thread managment
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Phillip Susi
10 * Skywing
11 */
12
13 /*
14 * NOTE:
15 *
16 * All of the routines that manipulate the thread queue synchronize on
17 * a single spinlock
18 *
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ntoskrnl.h>
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* GLOBAL *******************************************************************/
28
29 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
30
31 static ULONG PiThreadNotifyRoutineCount = 0;
32 static PCREATE_THREAD_NOTIFY_ROUTINE
33 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
34
35 ULONG
36 STDCALL
37 KeSuspendThread(PKTHREAD Thread);
38 /* FUNCTIONS ***************************************************************/
39
40 VOID
41 PiBeforeBeginThread(CONTEXT c)
42 {
43 KeLowerIrql(PASSIVE_LEVEL);
44 }
45
46 NTSTATUS
47 PsInitializeThread(PEPROCESS Process,
48 PETHREAD* ThreadPtr,
49 POBJECT_ATTRIBUTES ObjectAttributes,
50 KPROCESSOR_MODE AccessMode,
51 BOOLEAN First)
52 {
53 PETHREAD Thread;
54 NTSTATUS Status;
55 KIRQL oldIrql;
56
57 PAGED_CODE();
58
59 if (Process == NULL)
60 {
61 Process = PsInitialSystemProcess;
62 }
63
64 /*
65 * Create and initialize thread
66 */
67 Status = ObCreateObject(AccessMode,
68 PsThreadType,
69 ObjectAttributes,
70 KernelMode,
71 NULL,
72 sizeof(ETHREAD),
73 0,
74 0,
75 (PVOID*)&Thread);
76 if (!NT_SUCCESS(Status))
77 {
78 return(Status);
79 }
80
81 /*
82 * Reference process
83 */
84 ObReferenceObjectByPointer(Process,
85 PROCESS_CREATE_THREAD,
86 PsProcessType,
87 KernelMode);
88
89 Thread->ThreadsProcess = Process;
90 Thread->Cid.UniqueThread = NULL;
91 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
92
93 DPRINT("Thread = %x\n",Thread);
94
95 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
96 InitializeListHead(&Thread->ActiveTimerListHead);
97 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
98 InitializeListHead(&Thread->IrpList);
99 Thread->DeadThread = FALSE;
100 Thread->HasTerminated = FALSE;
101 Thread->Tcb.Win32Thread = NULL;
102 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
103
104
105 Thread->Tcb.BasePriority = (CHAR)Process->Pcb.BasePriority;
106 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
107
108 /*
109 * Local Procedure Call facility (LPC)
110 */
111 KeInitializeSemaphore (& Thread->LpcReplySemaphore, 0, LONG_MAX);
112 Thread->LpcReplyMessage = NULL;
113 Thread->LpcReplyMessageId = 0; /* not valid */
114 /* Thread->LpcReceiveMessageId = 0; */
115 Thread->LpcExitThreadCalled = FALSE;
116 Thread->LpcReceivedMsgIdValid = FALSE;
117
118 oldIrql = KeAcquireDispatcherDatabaseLock();
119 InsertTailList(&Process->ThreadListHead,
120 &Thread->ThreadListEntry);
121 KeReleaseDispatcherDatabaseLock(oldIrql);
122
123 *ThreadPtr = Thread;
124
125 return STATUS_SUCCESS;
126 }
127
128
129 static NTSTATUS
130 PsCreateTeb(HANDLE ProcessHandle,
131 PTEB *TebPtr,
132 PETHREAD Thread,
133 PINITIAL_TEB InitialTeb)
134 {
135 PEPROCESS Process;
136 NTSTATUS Status;
137 ULONG ByteCount;
138 ULONG RegionSize;
139 ULONG TebSize;
140 PVOID TebBase;
141 TEB Teb;
142
143 PAGED_CODE();
144
145 TebSize = PAGE_SIZE;
146
147 if (NULL == Thread->ThreadsProcess)
148 {
149 /* We'll be allocating a 64k block here and only use 4k of it, but this
150 path should almost never be taken. Actually, I never saw it was taken,
151 so maybe we should just ASSERT(NULL != Thread->ThreadsProcess) and
152 move on */
153 TebBase = NULL;
154 Status = ZwAllocateVirtualMemory(ProcessHandle,
155 &TebBase,
156 0,
157 &TebSize,
158 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
159 PAGE_READWRITE);
160 if (! NT_SUCCESS(Status))
161 {
162 DPRINT1("Failed to allocate virtual memory for TEB\n");
163 return Status;
164 }
165 }
166 else
167 {
168 Process = Thread->ThreadsProcess;
169 ExAcquireFastMutex(&Process->TebLock);
170 if (NULL == Process->TebBlock ||
171 Process->TebBlock == Process->TebLastAllocated)
172 {
173 Process->TebBlock = NULL;
174 RegionSize = MM_VIRTMEM_GRANULARITY;
175 Status = ZwAllocateVirtualMemory(ProcessHandle,
176 &Process->TebBlock,
177 0,
178 &RegionSize,
179 MEM_RESERVE | MEM_TOP_DOWN,
180 PAGE_READWRITE);
181 if (! NT_SUCCESS(Status))
182 {
183 ExReleaseFastMutex(&Process->TebLock);
184 DPRINT1("Failed to reserve virtual memory for TEB\n");
185 return Status;
186 }
187 Process->TebLastAllocated = (PVOID) ((char *) Process->TebBlock + RegionSize);
188 }
189 TebBase = (PVOID) ((char *) Process->TebLastAllocated - PAGE_SIZE);
190 Status = ZwAllocateVirtualMemory(ProcessHandle,
191 &TebBase,
192 0,
193 &TebSize,
194 MEM_COMMIT,
195 PAGE_READWRITE);
196 if (! NT_SUCCESS(Status))
197 {
198 DPRINT1("Failed to commit virtual memory for TEB\n");
199 return Status;
200 }
201 Process->TebLastAllocated = TebBase;
202 ExReleaseFastMutex(&Process->TebLock);
203 }
204
205 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
206 ASSERT(NULL != TebBase && PAGE_SIZE <= TebSize);
207
208 RtlZeroMemory(&Teb, sizeof(TEB));
209 /* set all pointers to and from the TEB */
210 Teb.Tib.Self = TebBase;
211 if (Thread->ThreadsProcess)
212 {
213 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
214 }
215 DPRINT("Teb.Peb %x\n", Teb.Peb);
216
217 /* store stack information from InitialTeb */
218 if(InitialTeb != NULL)
219 {
220 /* fixed-size stack */
221 if(InitialTeb->StackBase && InitialTeb->StackLimit)
222 {
223 Teb.Tib.StackBase = InitialTeb->StackBase;
224 Teb.Tib.StackLimit = InitialTeb->StackLimit;
225 Teb.DeallocationStack = InitialTeb->StackLimit;
226 }
227 /* expandable stack */
228 else
229 {
230 Teb.Tib.StackBase = InitialTeb->StackCommit;
231 Teb.Tib.StackLimit = InitialTeb->StackCommitMax;
232 Teb.DeallocationStack = InitialTeb->StackReserved;
233 }
234 }
235
236 /* more initialization */
237 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
238 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
239 Teb.CurrentLocale = PsDefaultThreadLocaleId;
240
241 /* Terminate the exception handler list */
242 Teb.Tib.ExceptionList = (PVOID)-1;
243
244 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
245
246 /* write TEB data into teb page */
247 Status = NtWriteVirtualMemory(ProcessHandle,
248 TebBase,
249 &Teb,
250 sizeof(TEB),
251 &ByteCount);
252
253 if (!NT_SUCCESS(Status))
254 {
255 /* free TEB */
256 DPRINT1 ("Writing TEB failed!\n");
257
258 RegionSize = 0;
259 NtFreeVirtualMemory(ProcessHandle,
260 TebBase,
261 &RegionSize,
262 MEM_RELEASE);
263
264 return Status;
265 }
266
267 if (TebPtr != NULL)
268 {
269 *TebPtr = (PTEB)TebBase;
270 }
271
272 DPRINT("TEB allocated at %p\n", TebBase);
273
274 return Status;
275 }
276
277
278 VOID STDCALL
279 LdrInitApcRundownRoutine(PKAPC Apc)
280 {
281 ExFreePool(Apc);
282 }
283
284
285 VOID STDCALL
286 LdrInitApcKernelRoutine(PKAPC Apc,
287 PKNORMAL_ROUTINE* NormalRoutine,
288 PVOID* NormalContext,
289 PVOID* SystemArgument1,
290 PVOID* SystemArgument2)
291 {
292 ExFreePool(Apc);
293 }
294
295
296 NTSTATUS STDCALL
297 NtCreateThread(OUT PHANDLE ThreadHandle,
298 IN ACCESS_MASK DesiredAccess,
299 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
300 IN HANDLE ProcessHandle,
301 OUT PCLIENT_ID ClientId,
302 IN PCONTEXT ThreadContext,
303 IN PINITIAL_TEB InitialTeb,
304 IN BOOLEAN CreateSuspended)
305 {
306 HANDLE hThread;
307 CONTEXT SafeContext;
308 INITIAL_TEB SafeInitialTeb;
309 PEPROCESS Process;
310 PETHREAD Thread;
311 PTEB TebBase;
312 PKAPC LdrInitApc;
313 KIRQL oldIrql;
314 KPROCESSOR_MODE PreviousMode;
315 NTSTATUS Status = STATUS_SUCCESS;
316
317 PAGED_CODE();
318
319 if(ThreadContext == NULL)
320 {
321 return STATUS_INVALID_PARAMETER;
322 }
323
324 PreviousMode = ExGetPreviousMode();
325
326 if(PreviousMode != KernelMode)
327 {
328 _SEH_TRY
329 {
330 ProbeForWrite(ThreadHandle,
331 sizeof(HANDLE),
332 sizeof(ULONG));
333 if(ClientId != NULL)
334 {
335 ProbeForWrite(ClientId,
336 sizeof(CLIENT_ID),
337 sizeof(ULONG));
338 }
339 ProbeForRead(ThreadContext,
340 sizeof(CONTEXT),
341 sizeof(ULONG));
342 SafeContext = *ThreadContext;
343 ThreadContext = &SafeContext;
344 ProbeForRead(InitialTeb,
345 sizeof(INITIAL_TEB),
346 sizeof(ULONG));
347 SafeInitialTeb = *InitialTeb;
348 InitialTeb = &SafeInitialTeb;
349 }
350 _SEH_HANDLE
351 {
352 Status = _SEH_GetExceptionCode();
353 }
354 _SEH_END;
355
356 if(!NT_SUCCESS(Status))
357 {
358 return Status;
359 }
360 }
361
362 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
363 ThreadHandle,ThreadContext);
364
365 Status = ObReferenceObjectByHandle(ProcessHandle,
366 PROCESS_CREATE_THREAD,
367 PsProcessType,
368 PreviousMode,
369 (PVOID*)&Process,
370 NULL);
371 if(!NT_SUCCESS(Status))
372 {
373 return(Status);
374 }
375
376 Status = PsLockProcess(Process, FALSE);
377 if (!NT_SUCCESS(Status))
378 {
379 ObDereferenceObject(Process);
380 return(Status);
381 }
382
383 if(Process->ExitTime.QuadPart != 0)
384 {
385 PsUnlockProcess(Process);
386 return STATUS_PROCESS_IS_TERMINATING;
387 }
388
389 PsUnlockProcess(Process);
390
391 Status = PsInitializeThread(Process,
392 &Thread,
393 ObjectAttributes,
394 PreviousMode,
395 FALSE);
396
397 ObDereferenceObject(Process);
398
399 if (!NT_SUCCESS(Status))
400 {
401 return(Status);
402 }
403
404 /* create a client id handle */
405 Status = PsCreateCidHandle(Thread, PsThreadType, &Thread->Cid.UniqueThread);
406 if (!NT_SUCCESS(Status))
407 {
408 ObDereferenceObject(Thread);
409 return Status;
410 }
411
412 Status = KiArchInitThreadWithContext(&Thread->Tcb, ThreadContext);
413 if (!NT_SUCCESS(Status))
414 {
415 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
416 ObDereferenceObject(Thread);
417 return(Status);
418 }
419
420 Status = PsCreateTeb(ProcessHandle,
421 &TebBase,
422 Thread,
423 InitialTeb);
424 if (!NT_SUCCESS(Status))
425 {
426 PsDeleteCidHandle(Thread->Cid.UniqueThread, PsThreadType);
427 ObDereferenceObject(Thread);
428 return(Status);
429 }
430 Thread->Tcb.Teb = TebBase;
431
432 Thread->StartAddress = NULL;
433
434 /*
435 * Maybe send a message to the process's debugger
436 */
437 DbgkCreateThread((PVOID)ThreadContext->Eip);
438
439 /*
440 * First, force the thread to be non-alertable for user-mode alerts.
441 */
442 Thread->Tcb.Alertable = FALSE;
443
444 /*
445 * If the thread is to be created suspended then queue an APC to
446 * do the suspend before we run any userspace code.
447 */
448 if (CreateSuspended)
449 {
450 KeSuspendThread(&Thread->Tcb);
451 }
452
453 /*
454 * Queue an APC to the thread that will execute the ntdll startup
455 * routine.
456 */
457 LdrInitApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
458 KeInitializeApc(LdrInitApc, &Thread->Tcb, OriginalApcEnvironment, LdrInitApcKernelRoutine,
459 LdrInitApcRundownRoutine, LdrpGetSystemDllEntryPoint(),
460 UserMode, NULL);
461 KeInsertQueueApc(LdrInitApc, NULL, NULL, IO_NO_INCREMENT);
462
463 /*
464 * The thread is non-alertable, so the APC we added did not set UserApcPending to TRUE.
465 * We must do this manually. Do NOT attempt to set the Thread to Alertable before the call,
466 * doing so is a blatant and erronous hack.
467 */
468 Thread->Tcb.ApcState.UserApcPending = TRUE;
469 Thread->Tcb.Alerted[KernelMode] = TRUE;
470
471 oldIrql = KeAcquireDispatcherDatabaseLock ();
472 KiUnblockThread(&Thread->Tcb, NULL, 0);
473 KeReleaseDispatcherDatabaseLock(oldIrql);
474
475 Status = ObInsertObject((PVOID)Thread,
476 NULL,
477 DesiredAccess,
478 0,
479 NULL,
480 &hThread);
481 if(NT_SUCCESS(Status))
482 {
483 _SEH_TRY
484 {
485 if(ClientId != NULL)
486 {
487 *ClientId = Thread->Cid;
488 }
489 *ThreadHandle = hThread;
490 }
491 _SEH_HANDLE
492 {
493 Status = _SEH_GetExceptionCode();
494 }
495 _SEH_END;
496 }
497
498 return Status;
499 }
500
501
502 /*
503 * @implemented
504 */
505 NTSTATUS STDCALL
506 PsCreateSystemThread(PHANDLE ThreadHandle,
507 ACCESS_MASK DesiredAccess,
508 POBJECT_ATTRIBUTES ObjectAttributes,
509 HANDLE ProcessHandle,
510 PCLIENT_ID ClientId,
511 PKSTART_ROUTINE StartRoutine,
512 PVOID StartContext)
513 /*
514 * FUNCTION: Creates a thread which executes in kernel mode
515 * ARGUMENTS:
516 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
517 * handle
518 * DesiredAccess = Requested access to the thread
519 * ObjectAttributes = Object attributes (optional)
520 * ProcessHandle = Handle of process thread will run in
521 * NULL to use system process
522 * ClientId (OUT) = Caller supplied storage for the returned client id
523 * of the thread (optional)
524 * StartRoutine = Entry point for the thread
525 * StartContext = Argument supplied to the thread when it begins
526 * execution
527 * RETURNS: Success or failure status
528 */
529 {
530 PETHREAD Thread;
531 NTSTATUS Status;
532 KIRQL oldIrql;
533
534 PAGED_CODE();
535
536 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
537 ThreadHandle,ProcessHandle);
538
539 Status = PsInitializeThread(NULL,
540 &Thread,
541 ObjectAttributes,
542 KernelMode,
543 FALSE);
544 if (!NT_SUCCESS(Status))
545 {
546 return(Status);
547 }
548
549 /* Set the thread as a system thread */
550 Thread->SystemThread = TRUE;
551
552 Status = PsCreateCidHandle(Thread,
553 PsThreadType,
554 &Thread->Cid.UniqueThread);
555 if(!NT_SUCCESS(Status))
556 {
557 ObDereferenceObject(Thread);
558 return Status;
559 }
560
561 Thread->StartAddress = StartRoutine;
562 Status = KiArchInitThread(&Thread->Tcb, StartRoutine, StartContext);
563 if (!NT_SUCCESS(Status))
564 {
565 ObDereferenceObject(Thread);
566 return(Status);
567 }
568
569 if (ClientId != NULL)
570 {
571 *ClientId=Thread->Cid;
572 }
573
574 oldIrql = KeAcquireDispatcherDatabaseLock ();
575 KiUnblockThread(&Thread->Tcb, NULL, 0);
576 KeReleaseDispatcherDatabaseLock(oldIrql);
577
578 Status = ObInsertObject((PVOID)Thread,
579 NULL,
580 DesiredAccess,
581 0,
582 NULL,
583 ThreadHandle);
584
585 /* don't dereference the thread, the initial reference serves as the keep-alive
586 reference which will be removed by the thread reaper */
587
588 return Status;
589 }
590
591
592 VOID STDCALL
593 PspRunCreateThreadNotifyRoutines(PETHREAD CurrentThread,
594 BOOLEAN Create)
595 {
596 ULONG i;
597 CLIENT_ID Cid = CurrentThread->Cid;
598
599 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
600 {
601 PiThreadNotifyRoutine[i](Cid.UniqueProcess, Cid.UniqueThread, Create);
602 }
603 }
604
605
606 /*
607 * @implemented
608 */
609 NTSTATUS STDCALL
610 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
611 {
612 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
613 {
614 return(STATUS_INSUFFICIENT_RESOURCES);
615 }
616
617 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
618 PiThreadNotifyRoutineCount++;
619
620 return(STATUS_SUCCESS);
621 }
622
623 /* EOF */