Added multiboot support to ntoskrnl
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.23 2000/10/07 13:41:54 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 #include <internal/id.h>
32
33 #define NDEBUG
34 #include <internal/debug.h>
35
36 /* GLOBAL *******************************************************************/
37
38 static ULONG PiNextThreadUniqueId = 0;
39
40 extern KSPIN_LOCK PiThreadListLock;
41 extern ULONG PiNrThreads;
42
43 extern LIST_ENTRY PiThreadListHead;
44
45 /* FUNCTIONS ***************************************************************/
46
47 NTSTATUS 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 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 DPRINT( "Creating thread in process %x\n", Process );
342 }
343 else
344 {
345 Process = PsInitialSystemProcess;
346 ObReferenceObjectByPointer(Process,
347 PROCESS_CREATE_THREAD,
348 PsProcessType,
349 UserMode);
350 }
351
352 /*
353 * Create and initialize thread
354 */
355 Thread = ObCreateObject(ThreadHandle,
356 DesiredAccess,
357 ThreadAttributes,
358 PsThreadType);
359 DPRINT("Thread = %x\n",Thread);
360
361 PiNrThreads++;
362
363 KeInitializeThread(&Process->Pcb, &Thread->Tcb);
364 Thread->ThreadsProcess = Process;
365 /*
366 * FIXME: What lock protects this?
367 */
368 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
369 &Thread->Tcb.ProcessThreadListEntry);
370 InitializeListHead(&Thread->TerminationPortList);
371 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
372 InitializeListHead(&Thread->IrpList);
373 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
374 &PiNextThreadUniqueId);
375 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
376 Thread->DeadThread = 0;
377 Thread->Win32ThreadData = 0;
378 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
379
380 *ThreadPtr = Thread;
381
382 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
383 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
384 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
385
386 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
387 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
388
389 return(STATUS_SUCCESS);
390 }
391
392
393 static NTSTATUS PsCreateTeb (HANDLE ProcessHandle,
394 PNT_TEB *TebPtr,
395 PETHREAD Thread,
396 PINITIAL_TEB InitialTeb)
397 {
398 MEMORY_BASIC_INFORMATION Info;
399 NTSTATUS Status;
400 ULONG ByteCount;
401 ULONG RegionSize;
402 ULONG TebSize;
403 PVOID TebBase;
404 NT_TEB Teb;
405 ULONG ResultLength;
406
407 TebBase = (PVOID)0x7FFDE000;
408 TebSize = PAGESIZE;
409
410 while (TRUE)
411 {
412 Status = NtQueryVirtualMemory(ProcessHandle,
413 TebBase,
414 MemoryBasicInformation,
415 &Info,
416 sizeof(Info),
417 &ResultLength);
418 if (!NT_SUCCESS(Status))
419 {
420 DbgPrint("NtQueryVirtualMemory (Status %x)\n", Status);
421 KeBugCheck(0);
422 }
423 if (Info.State == MEM_FREE)
424 {
425 /* The TEB must reside in user space */
426 Status = NtAllocateVirtualMemory(ProcessHandle,
427 &TebBase,
428 0,
429 &TebSize,
430 MEM_COMMIT,
431 PAGE_READWRITE);
432 if (NT_SUCCESS(Status))
433 {
434 break;
435 }
436 }
437
438 TebBase = TebBase - TebSize;
439 }
440
441 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
442
443 /* set all pointers to and from the TEB */
444 Teb.Tib.Self = TebBase;
445 if (Thread->ThreadsProcess)
446 {
447 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
448 }
449 // DPRINT1("Teb.Peb %x\n", Teb.Peb);
450
451 /* store stack information from InitialTeb */
452 if (InitialTeb != NULL)
453 {
454 Teb.Tib.StackBase = InitialTeb->StackBase;
455 Teb.Tib.StackLimit = InitialTeb->StackLimit;
456
457 /*
458 * I don't know if this is really stored in a WNT-TEB,
459 * but it's needed to free the thread stack. (Eric Kohl)
460 */
461 Teb.StackCommit = InitialTeb->StackCommit;
462 Teb.StackCommitMax = InitialTeb->StackCommitMax;
463 Teb.StackReserved = InitialTeb->StackReserved;
464 }
465
466
467 /* more initialization */
468 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
469 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
470
471 DPRINT("sizeof(NT_TEB) %x\n", sizeof(NT_TEB));
472
473 /* write TEB data into teb page */
474 Status = NtWriteVirtualMemory(ProcessHandle,
475 TebBase,
476 &Teb,
477 sizeof(NT_TEB),
478 &ByteCount);
479
480 if (!NT_SUCCESS(Status))
481 {
482 /* free TEB */
483 DPRINT1 ("Writing TEB failed!\n");
484
485 RegionSize = 0;
486 NtFreeVirtualMemory(ProcessHandle,
487 TebBase,
488 &RegionSize,
489 MEM_RELEASE);
490
491 return Status;
492 }
493
494 if (TebPtr != NULL)
495 {
496 *TebPtr = (PNT_TEB)TebBase;
497 }
498
499 // DPRINT1 ("TEB allocated at %p\n", TebBase);
500
501 return Status;
502 }
503
504
505 NTSTATUS STDCALL NtCreateThread (PHANDLE ThreadHandle,
506 ACCESS_MASK DesiredAccess,
507 POBJECT_ATTRIBUTES ObjectAttributes,
508 HANDLE ProcessHandle,
509 PCLIENT_ID Client,
510 PCONTEXT ThreadContext,
511 PINITIAL_TEB InitialTeb,
512 BOOLEAN CreateSuspended)
513 {
514 PETHREAD Thread;
515 PNT_TEB TebBase;
516 NTSTATUS Status;
517
518 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
519 ThreadHandle,ThreadContext);
520
521 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
522 DesiredAccess,ObjectAttributes);
523 if (!NT_SUCCESS(Status))
524 {
525 return(Status);
526 }
527
528 #if 0
529 Status = NtWriteVirtualMemory(ProcessHandle,
530 (PVOID)(((ULONG)ThreadContext->Esp) - 8),
531 &ThreadContext->Eip,
532 sizeof(ULONG),
533 &Length);
534 if (!NT_SUCCESS(Status))
535 {
536 DPRINT1("NtWriteVirtualMemory failed\n");
537 KeBugCheck(0);
538 }
539 ThreadContext->Eip = LdrpGetSystemDllEntryPoint;
540 #endif
541
542 Status = HalInitTaskWithContext(Thread,ThreadContext);
543 if (!NT_SUCCESS(Status))
544 {
545 return(Status);
546 }
547
548 Status = PsCreateTeb (ProcessHandle,
549 &TebBase,
550 Thread,
551 InitialTeb);
552 if (!NT_SUCCESS(Status))
553 {
554 return(Status);
555 }
556
557 /* Attention: TebBase is in user memory space */
558 Thread->Tcb.Teb = TebBase;
559
560 Thread->StartAddress=NULL;
561
562 if (Client!=NULL)
563 {
564 *Client=Thread->Cid;
565 }
566
567 /*
568 * Maybe send a message to the process's debugger
569 */
570 #if 0
571 if (ParentProcess->DebugPort != NULL)
572 {
573 LPC_DBG_MESSAGE Message;
574 PEPROCESS Process;
575
576
577 Message.Header.MessageSize = sizeof(LPC_DBG_MESSAGE);
578 Message.Header.DataSize = sizeof(LPC_DBG_MESSAGE) -
579 sizeof(LPC_MESSAGE_HEADER);
580 Message.EventCode = DBG_EVENT_CREATE_THREAD;
581 Message.Data.CreateThread.StartAddress =
582 ;
583 Message.Data.CreateProcess.Base = ImageBase;
584 Message.Data.CreateProcess.EntryPoint = NULL; //
585
586 Status = LpcSendDebugMessagePort(ParentProcess->DebugPort,
587 &Message);
588 }
589 #endif
590
591 if (!CreateSuspended)
592 {
593 DPRINT("Not creating suspended\n");
594 PsUnfreezeThread(Thread, NULL);
595 }
596 DPRINT("Thread %x\n", Thread);
597 DPRINT("ObGetReferenceCount(Thread) %d ObGetHandleCount(Thread) %x\n",
598 ObGetReferenceCount(Thread), ObGetHandleCount(Thread));
599 DPRINT("Finished PsCreateThread()\n");
600 return(STATUS_SUCCESS);
601 }
602
603
604 NTSTATUS STDCALL PsCreateSystemThread(PHANDLE ThreadHandle,
605 ACCESS_MASK DesiredAccess,
606 POBJECT_ATTRIBUTES ObjectAttributes,
607 HANDLE ProcessHandle,
608 PCLIENT_ID ClientId,
609 PKSTART_ROUTINE StartRoutine,
610 PVOID StartContext)
611 /*
612 * FUNCTION: Creates a thread which executes in kernel mode
613 * ARGUMENTS:
614 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
615 * handle
616 * DesiredAccess = Requested access to the thread
617 * ObjectAttributes = Object attributes (optional)
618 * ProcessHandle = Handle of process thread will run in
619 * NULL to use system process
620 * ClientId (OUT) = Caller supplied storage for the returned client id
621 * of the thread (optional)
622 * StartRoutine = Entry point for the thread
623 * StartContext = Argument supplied to the thread when it begins
624 * execution
625 * RETURNS: Success or failure status
626 */
627 {
628 PETHREAD Thread;
629 NTSTATUS Status;
630
631 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
632 ThreadHandle,ProcessHandle);
633
634 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
635 DesiredAccess,ObjectAttributes);
636 if (!NT_SUCCESS(Status))
637 {
638 return(Status);
639 }
640
641 Thread->StartAddress=StartRoutine;
642 Status = HalInitTask(Thread,StartRoutine,StartContext);
643 if (!NT_SUCCESS(Status))
644 {
645 return(Status);
646 }
647
648 if (ClientId!=NULL)
649 {
650 *ClientId=Thread->Cid;
651 }
652
653 PsUnfreezeThread(Thread, NULL);
654
655 return(STATUS_SUCCESS);
656 }
657