Corrected bug when booting with loadros
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.17 2000/06/27 19:20:45 dwelch Exp $
2 *
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)
8 * REVISION HISTORY:
9 * 23/06/98: Created
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
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 <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <string.h>
27 #include <internal/string.h>
28 #include <internal/hal.h>
29 #include <internal/ps.h>
30 #include <internal/ob.h>
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* GLOBAL *******************************************************************/
36
37 static ULONG PiNextThreadUniqueId = 0;
38
39 extern KSPIN_LOCK PiThreadListLock;
40 extern ULONG PiNrThreads;
41
42 extern LIST_ENTRY PiThreadListHead;
43
44 /* FUNCTIONS ***************************************************************/
45
46 NTSTATUS
47 STDCALL
48 PsAssignImpersonationToken(PETHREAD Thread,
49 HANDLE TokenHandle)
50 {
51 PACCESS_TOKEN Token;
52 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
53 NTSTATUS Status;
54
55 if (TokenHandle != NULL)
56 {
57 Status = ObReferenceObjectByHandle(TokenHandle,
58 0,
59 SeTokenType,
60 UserMode,
61 (PVOID*)&Token,
62 NULL);
63 if (!NT_SUCCESS(Status))
64 {
65 return(Status);
66 }
67 ImpersonationLevel = Token->ImpersonationLevel;
68 }
69 else
70 {
71 Token = NULL;
72 ImpersonationLevel = 0;
73 }
74
75 PsImpersonateClient(Thread,
76 Token,
77 0,
78 0,
79 ImpersonationLevel);
80 if (Token != NULL)
81 {
82 ObDereferenceObject(Token);
83 }
84 return(STATUS_SUCCESS);
85 }
86
87 VOID STDCALL PsRevertToSelf(PETHREAD Thread)
88 {
89 if (Thread->ActiveImpersonationInfo != 0)
90 {
91 Thread->ActiveImpersonationInfo = 0;
92 ObDereferenceObject(Thread->ImpersonationInfo->Token);
93 }
94 }
95
96 VOID STDCALL PsImpersonateClient(PETHREAD Thread,
97 PACCESS_TOKEN Token,
98 UCHAR b,
99 UCHAR c,
100 SECURITY_IMPERSONATION_LEVEL Level)
101 {
102 if (Token == 0)
103 {
104 if (Thread->ActiveImpersonationInfo != 0)
105 {
106 Thread->ActiveImpersonationInfo = 0;
107 if (Thread->ImpersonationInfo->Token != NULL)
108 {
109 ObDereferenceObject(Thread->ImpersonationInfo->Token);
110 }
111 }
112 return;
113 }
114 if (Thread->ActiveImpersonationInfo == 0 ||
115 Thread->ImpersonationInfo == NULL)
116 {
117 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
118 sizeof(PS_IMPERSONATION_INFO));
119 }
120 Thread->ImpersonationInfo->Level = Level;
121 Thread->ImpersonationInfo->Unknown2 = c;
122 Thread->ImpersonationInfo->Unknown1 = b;
123 Thread->ImpersonationInfo->Token = Token;
124 ObReferenceObjectByPointer(Token,
125 0,
126 SeTokenType,
127 KernelMode);
128 Thread->ActiveImpersonationInfo = 1;
129 }
130
131 PACCESS_TOKEN PsReferenceEffectiveToken(PETHREAD Thread,
132 PTOKEN_TYPE TokenType,
133 PUCHAR b,
134 PSECURITY_IMPERSONATION_LEVEL Level)
135 {
136 PEPROCESS Process;
137 PACCESS_TOKEN Token;
138
139 if (Thread->ActiveImpersonationInfo == 0)
140 {
141 Process = Thread->ThreadsProcess;
142 *TokenType = TokenPrimary;
143 *b = 0;
144 Token = Process->Token;
145 }
146 else
147 {
148 Token = Thread->ImpersonationInfo->Token;
149 *TokenType = TokenImpersonation;
150 *b = Thread->ImpersonationInfo->Unknown2;
151 *Level = Thread->ImpersonationInfo->Level;
152 }
153 return(Token);
154 }
155
156 NTSTATUS STDCALL NtImpersonateThread (IN HANDLE ThreadHandle,
157 IN HANDLE ThreadToImpersonateHandle,
158 IN PSECURITY_QUALITY_OF_SERVICE
159 SecurityQualityOfService)
160 {
161 PETHREAD Thread;
162 PETHREAD ThreadToImpersonate;
163 NTSTATUS Status;
164 SE_SOME_STRUCT2 b;
165
166 Status = ObReferenceObjectByHandle(ThreadHandle,
167 0,
168 PsThreadType,
169 UserMode,
170 (PVOID*)&Thread,
171 NULL);
172 if (!NT_SUCCESS(Status))
173 {
174 return(Status);
175 }
176
177 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
178 0,
179 PsThreadType,
180 UserMode,
181 (PVOID*)&ThreadToImpersonate,
182 NULL);
183 if (!NT_SUCCESS(Status))
184 {
185 ObDereferenceObject(Thread);
186 return(Status);
187 }
188
189 Status = SeCreateClientSecurity(ThreadToImpersonate,
190 SecurityQualityOfService,
191 0,
192 &b);
193 if (!NT_SUCCESS(Status))
194 {
195 ObDereferenceObject(Thread);
196 ObDereferenceObject(ThreadToImpersonate);
197 return(Status);
198 }
199
200 SeImpersonateClient(&b, Thread);
201 if (b.Token != NULL)
202 {
203 ObDereferenceObject(b.Token);
204 }
205 return(STATUS_SUCCESS);
206 }
207
208 NTSTATUS STDCALL NtOpenThreadToken(IN HANDLE ThreadHandle,
209 IN ACCESS_MASK DesiredAccess,
210 IN BOOLEAN OpenAsSelf,
211 OUT PHANDLE TokenHandle)
212 {
213 #if 0
214 PETHREAD Thread;
215 NTSTATUS Status;
216 PACCESS_TOKEN Token;
217
218 Status = ObReferenceObjectByHandle(ThreadHandle,
219 0,
220 PsThreadType,
221 UserMode,
222 (PVOID*)&Thread,
223 NULL);
224 if (!NT_SUCCESS(Status))
225 {
226 return(Status);
227 }
228
229 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
230 SepCreateImpersonationTokenDacl(Token);
231 #endif
232 return(STATUS_UNSUCCESSFUL);
233 }
234
235 PACCESS_TOKEN STDCALL PsReferenceImpersonationToken(PETHREAD Thread,
236 PULONG Unknown1,
237 PULONG Unknown2,
238 SECURITY_IMPERSONATION_LEVEL*
239 Level)
240 {
241 if (Thread->ActiveImpersonationInfo == 0)
242 {
243 return(NULL);
244 }
245
246 *Level = Thread->ImpersonationInfo->Level;
247 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
248 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
249 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
250 GENERIC_ALL,
251 SeTokenType,
252 KernelMode);
253 return(Thread->ImpersonationInfo->Token);
254 }
255
256 static VOID PiTimeoutThread(struct _KDPC *dpc,
257 PVOID Context,
258 PVOID arg1,
259 PVOID arg2 )
260 {
261 // wake up the thread, and tell it it timed out
262 NTSTATUS Status = STATUS_TIMEOUT;
263
264 DPRINT("PiTimeoutThread()\n");
265
266 KeRemoveAllWaitsThread((PETHREAD)Context, Status);
267 }
268
269 VOID PiBeforeBeginThread(CONTEXT c)
270 {
271 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
272 //KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
273 KeLowerIrql(PASSIVE_LEVEL);
274 }
275
276 VOID PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
277 {
278 NTSTATUS Ret;
279
280 // KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
281 KeLowerIrql(PASSIVE_LEVEL);
282 Ret = StartRoutine(StartContext);
283 PsTerminateSystemThread(Ret);
284 KeBugCheck(0);
285 }
286
287 VOID PiDeleteThread(PVOID ObjectBody)
288 {
289 KIRQL oldIrql;
290
291 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
292
293 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
294 DPRINT("Process %x(%d)\n", ((PETHREAD)ObjectBody)->ThreadsProcess,
295 ObGetReferenceCount(((PETHREAD)ObjectBody)->ThreadsProcess));
296 ObDereferenceObject(((PETHREAD)ObjectBody)->ThreadsProcess);
297 ((PETHREAD)ObjectBody)->ThreadsProcess = NULL;
298 PiNrThreads--;
299 RemoveEntryList(&((PETHREAD)ObjectBody)->Tcb.ThreadListEntry);
300 HalReleaseTask((PETHREAD)ObjectBody);
301 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
302 DPRINT("PiDeleteThread() finished\n");
303 }
304
305 VOID PiCloseThread(PVOID ObjectBody, ULONG HandleCount)
306 {
307 DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
308 DPRINT("ObGetReferenceCount(ObjectBody) %d "
309 "ObGetHandleCount(ObjectBody) %d\n",
310 ObGetReferenceCount(ObjectBody),
311 ObGetHandleCount(ObjectBody));
312 }
313
314 NTSTATUS PsInitializeThread(HANDLE ProcessHandle,
315 PETHREAD* ThreadPtr,
316 PHANDLE ThreadHandle,
317 ACCESS_MASK DesiredAccess,
318 POBJECT_ATTRIBUTES ThreadAttributes)
319 {
320 PETHREAD Thread;
321 NTSTATUS Status;
322 KIRQL oldIrql;
323 PEPROCESS Process;
324
325 /*
326 * Reference process
327 */
328 if (ProcessHandle != NULL)
329 {
330 Status = ObReferenceObjectByHandle(ProcessHandle,
331 PROCESS_CREATE_THREAD,
332 PsProcessType,
333 UserMode,
334 (PVOID*)&Process,
335 NULL);
336 if (Status != STATUS_SUCCESS)
337 {
338 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
339 return(Status);
340 }
341 }
342 else
343 {
344 Process = PsInitialSystemProcess;
345 ObReferenceObjectByPointer(Process,
346 PROCESS_CREATE_THREAD,
347 PsProcessType,
348 UserMode);
349 }
350
351 /*
352 * Create and initialize thread
353 */
354 Thread = ObCreateObject(ThreadHandle,
355 DesiredAccess,
356 ThreadAttributes,
357 PsThreadType);
358 DPRINT("Thread = %x\n",Thread);
359
360 PiNrThreads++;
361
362 Thread->Tcb.State = THREAD_STATE_SUSPENDED;
363 Thread->Tcb.SuspendCount = 0;
364 Thread->Tcb.FreezeCount = 1;
365 InitializeListHead(&Thread->Tcb.ApcState.ApcListHead[0]);
366 InitializeListHead(&Thread->Tcb.ApcState.ApcListHead[1]);
367 Thread->Tcb.KernelApcDisable = 1;
368 Thread->Tcb.WaitIrql = PASSIVE_LEVEL;
369 Thread->ThreadsProcess = Process;
370 KeInitializeDpc(&Thread->Tcb.TimerDpc, PiTimeoutThread, Thread);
371 Thread->Tcb.WaitBlockList = NULL;
372 InsertTailList(&Thread->ThreadsProcess->Pcb.ThreadListHead,
373 &Thread->Tcb.ProcessThreadListEntry);
374 InitializeListHead(&Thread->TerminationPortList);
375 KeInitializeDispatcherHeader(&Thread->Tcb.DispatcherHeader,
376 InternalThreadType,
377 sizeof(ETHREAD),
378 FALSE);
379 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
380 InitializeListHead(&Thread->IrpList);
381 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
382 &PiNextThreadUniqueId);
383 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
384 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
385
386 *ThreadPtr = Thread;
387
388 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
389 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
390 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
391
392 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
393 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
394
395 return(STATUS_SUCCESS);
396 }
397
398
399 static NTSTATUS PsCreateTeb (HANDLE ProcessHandle,
400 PNT_TEB *TebPtr,
401 PETHREAD Thread,
402 PINITIAL_TEB InitialTeb)
403 {
404 // MEMORY_BASIC_INFORMATION Info;
405 NTSTATUS Status;
406 ULONG ByteCount;
407 ULONG RegionSize;
408 ULONG TebSize;
409 PVOID TebBase;
410 NT_TEB Teb;
411
412 TebBase = (PVOID)0x7FFDE000;
413 TebSize = PAGESIZE;
414
415 while (TRUE)
416 {
417 /* The TEB must reside in user space */
418 Status = NtAllocateVirtualMemory(ProcessHandle,
419 &TebBase,
420 0,
421 &TebSize,
422 MEM_COMMIT,
423 PAGE_READWRITE);
424 if (NT_SUCCESS(Status))
425 {
426 // DPRINT1 ("TEB allocated at %x\n", TebBase);
427 break;
428 }
429 else
430 {
431 DPRINT ("TEB allocation failed! Status %x\n",Status);
432 for(;;);
433 return(Status);
434 }
435
436 // TebBase = Info.BaseAddress - TebSize;
437 TebBase = TebBase - TebSize;
438 }
439
440 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
441
442 /* set all pointers to and from the TEB */
443 Teb.Tib.Self = TebBase;
444 if (Thread->ThreadsProcess)
445 {
446 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
447 }
448 // DPRINT1("Teb.Peb %x\n", Teb.Peb);
449
450 /* store stack information from InitialTeb */
451 if (InitialTeb != NULL)
452 {
453 Teb.Tib.StackBase = InitialTeb->StackBase;
454 Teb.Tib.StackLimit = InitialTeb->StackLimit;
455
456 /*
457 * I don't know if this is really stored in a WNT-TEB,
458 * but it's needed to free the thread stack. (Eric Kohl)
459 */
460 Teb.StackCommit = InitialTeb->StackCommit;
461 Teb.StackCommitMax = InitialTeb->StackCommitMax;
462 Teb.StackReserved = InitialTeb->StackReserved;
463 }
464
465
466 /* more initialization */
467 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
468 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
469
470 DPRINT("sizeof(NT_TEB) %x\n", sizeof(NT_TEB));
471
472 /* write TEB data into teb page */
473 Status = NtWriteVirtualMemory(ProcessHandle,
474 TebBase,
475 &Teb,
476 sizeof(NT_TEB),
477 &ByteCount);
478
479 if (!NT_SUCCESS(Status))
480 {
481 /* free TEB */
482 DPRINT1 ("Writing TEB failed!\n");
483
484 RegionSize = 0;
485 NtFreeVirtualMemory(ProcessHandle,
486 TebBase,
487 &RegionSize,
488 MEM_RELEASE);
489
490 return Status;
491 }
492
493 if (TebPtr != NULL)
494 {
495 *TebPtr = (PNT_TEB)TebBase;
496 }
497
498 // DPRINT1 ("TEB allocated at %p\n", TebBase);
499
500 return Status;
501 }
502
503
504 NTSTATUS STDCALL NtCreateThread (PHANDLE ThreadHandle,
505 ACCESS_MASK DesiredAccess,
506 POBJECT_ATTRIBUTES ObjectAttributes,
507 HANDLE ProcessHandle,
508 PCLIENT_ID Client,
509 PCONTEXT ThreadContext,
510 PINITIAL_TEB InitialTeb,
511 BOOLEAN CreateSuspended)
512 {
513 PETHREAD Thread;
514 PNT_TEB TebBase;
515 NTSTATUS Status;
516
517 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
518 ThreadHandle,ThreadContext);
519
520 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
521 DesiredAccess,ObjectAttributes);
522 if (!NT_SUCCESS(Status))
523 {
524 return(Status);
525 }
526
527 Status = HalInitTaskWithContext(Thread,ThreadContext);
528 if (!NT_SUCCESS(Status))
529 {
530 return(Status);
531 }
532
533 Status = PsCreateTeb (ProcessHandle,
534 &TebBase,
535 Thread,
536 InitialTeb);
537 if (!NT_SUCCESS(Status))
538 {
539 return(Status);
540 }
541
542 /* Attention: TebBase is in user memory space */
543 Thread->Tcb.Teb = TebBase;
544
545 Thread->StartAddress=NULL;
546
547 if (Client!=NULL)
548 {
549 *Client=Thread->Cid;
550 }
551
552 /*
553 * Maybe send a message to the process's debugger
554 */
555 #if 0
556 if (ParentProcess->DebugPort != NULL)
557 {
558 LPC_DBG_MESSAGE Message;
559 PEPROCESS Process;
560
561
562 Message.Header.MessageSize = sizeof(LPC_DBG_MESSAGE);
563 Message.Header.DataSize = sizeof(LPC_DBG_MESSAGE) -
564 sizeof(LPC_MESSAGE_HEADER);
565 Message.EventCode = DBG_EVENT_CREATE_THREAD;
566 Message.Data.CreateThread.StartAddress =
567 ;
568 Message.Data.CreateProcess.Base = ImageBase;
569 Message.Data.CreateProcess.EntryPoint = NULL; //
570
571 Status = LpcSendDebugMessagePort(ParentProcess->DebugPort,
572 &Message);
573 }
574 #endif
575
576 if (!CreateSuspended)
577 {
578 DPRINT("Not creating suspended\n");
579 PsUnfreezeThread(Thread, NULL);
580 }
581 DPRINT("Thread %x\n", Thread);
582 DPRINT("ObGetReferenceCount(Thread) %d ObGetHandleCount(Thread) %x\n",
583 ObGetReferenceCount(Thread), ObGetHandleCount(Thread));
584 DPRINT("Finished PsCreateThread()\n");
585 return(STATUS_SUCCESS);
586 }
587
588
589 NTSTATUS STDCALL PsCreateSystemThread(PHANDLE ThreadHandle,
590 ACCESS_MASK DesiredAccess,
591 POBJECT_ATTRIBUTES ObjectAttributes,
592 HANDLE ProcessHandle,
593 PCLIENT_ID ClientId,
594 PKSTART_ROUTINE StartRoutine,
595 PVOID StartContext)
596 /*
597 * FUNCTION: Creates a thread which executes in kernel mode
598 * ARGUMENTS:
599 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
600 * handle
601 * DesiredAccess = Requested access to the thread
602 * ObjectAttributes = Object attributes (optional)
603 * ProcessHandle = Handle of process thread will run in
604 * NULL to use system process
605 * ClientId (OUT) = Caller supplied storage for the returned client id
606 * of the thread (optional)
607 * StartRoutine = Entry point for the thread
608 * StartContext = Argument supplied to the thread when it begins
609 * execution
610 * RETURNS: Success or failure status
611 */
612 {
613 PETHREAD Thread;
614 NTSTATUS Status;
615
616 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
617 ThreadHandle,ProcessHandle);
618
619 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
620 DesiredAccess,ObjectAttributes);
621 if (!NT_SUCCESS(Status))
622 {
623 return(Status);
624 }
625
626 Thread->StartAddress=StartRoutine;
627 Status = HalInitTask(Thread,StartRoutine,StartContext);
628 if (!NT_SUCCESS(Status))
629 {
630 return(Status);
631 }
632
633 if (ClientId!=NULL)
634 {
635 *ClientId=Thread->Cid;
636 }
637
638 PsUnfreezeThread(Thread, NULL);
639
640 return(STATUS_SUCCESS);
641 }
642