0e2d692f07af98e43ee999d02c99edde28d1dcf0
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.4 1999/12/20 02:14:40 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 static VOID PiTimeoutThread( struct _KDPC *dpc, PVOID Context, PVOID arg1, PVOID arg2 )
47 {
48 // wake up the thread, and tell it it timed out
49 NTSTATUS Status = STATUS_TIMEOUT;
50 PsUnfreezeThread( (ETHREAD *)Context, &Status );
51 }
52
53 VOID PiBeforeBeginThread(VOID)
54 {
55 DPRINT("PiBeforeBeginThread()\n");
56 //KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
57 KeLowerIrql(PASSIVE_LEVEL);
58 DPRINT("KeGetCurrentIrql() %d\n", KeGetCurrentIrql());
59 }
60
61 VOID PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
62 {
63 NTSTATUS Ret;
64
65 // KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
66 KeLowerIrql(PASSIVE_LEVEL);
67 Ret = StartRoutine(StartContext);
68 PsTerminateSystemThread(Ret);
69 KeBugCheck(0);
70 }
71
72 VOID PiDeleteThread(PVOID ObjectBody)
73 {
74 KIRQL oldIrql;
75
76 DPRINT1("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
77
78 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
79 ObDereferenceObject(((PETHREAD)ObjectBody)->ThreadsProcess);
80 ((PETHREAD)ObjectBody)->ThreadsProcess = NULL;
81 PiNrThreads--;
82 RemoveEntryList(&((PETHREAD)ObjectBody)->Tcb.ThreadListEntry);
83 HalReleaseTask((PETHREAD)ObjectBody);
84 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
85 DPRINT1("PiDeleteThread() finished\n");
86 }
87
88 VOID PiCloseThread(PVOID ObjectBody, ULONG HandleCount)
89 {
90 DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
91 DPRINT("ObGetReferenceCount(ObjectBody) %d "
92 "ObGetHandleCount(ObjectBody) %d\n",
93 ObGetReferenceCount(ObjectBody),
94 ObGetHandleCount(ObjectBody));
95 }
96
97 NTSTATUS PsInitializeThread(HANDLE ProcessHandle,
98 PETHREAD * ThreadPtr,
99 PHANDLE ThreadHandle,
100 ACCESS_MASK DesiredAccess,
101 POBJECT_ATTRIBUTES ThreadAttributes)
102 {
103 PETHREAD Thread;
104 NTSTATUS Status;
105 KIRQL oldIrql;
106 PEPROCESS Process;
107
108 /*
109 * Reference process
110 */
111 if (ProcessHandle != NULL)
112 {
113 Status = ObReferenceObjectByHandle(ProcessHandle,
114 PROCESS_CREATE_THREAD,
115 PsProcessType,
116 UserMode,
117 (PVOID*)&Process,
118 NULL);
119 if (Status != STATUS_SUCCESS)
120 {
121 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
122 return(Status);
123 }
124 }
125 else
126 {
127 Process = SystemProcess;
128 ObReferenceObjectByPointer(Process,
129 PROCESS_CREATE_THREAD,
130 PsProcessType,
131 UserMode);
132 }
133
134 /*
135 * Create and initialize thread
136 */
137 Thread = ObCreateObject(ThreadHandle,
138 DesiredAccess,
139 ThreadAttributes,
140 PsThreadType);
141 DPRINT("Thread = %x\n",Thread);
142
143 PiNrThreads++;
144
145 Thread->Tcb.State = THREAD_STATE_SUSPENDED;
146 Thread->Tcb.SuspendCount = 0;
147 Thread->Tcb.FreezeCount = 1;
148 InitializeListHead(&Thread->Tcb.ApcState.ApcListHead[0]);
149 InitializeListHead(&Thread->Tcb.ApcState.ApcListHead[1]);
150 Thread->Tcb.KernelApcDisable = 1;
151 Thread->Tcb.WaitIrql = PASSIVE_LEVEL;
152 Thread->ThreadsProcess = Process;
153 KeInitializeDpc( &Thread->Tcb.TimerDpc, PiTimeoutThread, Thread );
154 Thread->Tcb.WaitBlockList = NULL;
155 InsertTailList( &Thread->ThreadsProcess->Pcb.ThreadListHead, &Thread->Tcb.ProcessThreadListEntry );
156 KeInitializeDispatcherHeader(&Thread->Tcb.DispatcherHeader,
157 InternalThreadType,
158 sizeof(ETHREAD),
159 FALSE);
160
161 InitializeListHead(&Thread->IrpList);
162 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
163 &PiNextThreadUniqueId);
164 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
165 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
166
167 *ThreadPtr = Thread;
168
169 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
170 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
171 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
172
173 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
174 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
175
176 return(STATUS_SUCCESS);
177 }
178
179
180 static NTSTATUS PsCreateTeb (HANDLE ProcessHandle,
181 PNT_TEB *TebPtr,
182 PETHREAD Thread,
183 PINITIAL_TEB InitialTeb)
184 {
185 MEMORY_BASIC_INFORMATION Info;
186 NTSTATUS Status;
187 ULONG ByteCount;
188 ULONG RegionSize;
189 ULONG TebSize;
190 PVOID TebBase;
191 NT_TEB Teb;
192
193 TebBase = (PVOID)0x7FFDE000;
194 TebSize = PAGESIZE;
195
196 while (TRUE)
197 {
198 /* The TEB must reside in user space */
199 Status = NtAllocateVirtualMemory(ProcessHandle,
200 &TebBase,
201 0,
202 &TebSize,
203 MEM_COMMIT,
204 PAGE_READWRITE);
205 if (NT_SUCCESS(Status))
206 {
207 DPRINT ("TEB allocated at %x\n", TebBase);
208 break;
209 }
210 else
211 {
212 DPRINT ("TEB allocation failed! Status %x\n",Status);
213 }
214
215 TebBase = Info.BaseAddress - TebSize;
216 }
217
218 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
219
220 /* set all pointers to and from the TEB */
221 Teb.Tib.Self = TebBase;
222 if (Thread->ThreadsProcess)
223 {
224 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
225 }
226
227 /* store stack information from InitialTeb */
228 if (InitialTeb != NULL)
229 {
230 Teb.Tib.StackBase = InitialTeb->StackBase;
231 Teb.Tib.StackLimit = InitialTeb->StackLimit;
232
233 /*
234 * I don't know if this is really stored in a WNT-TEB,
235 * but it's needed to free the thread stack. (Eric Kohl)
236 */
237 Teb.StackCommit = InitialTeb->StackCommit;
238 Teb.StackCommitMax = InitialTeb->StackCommitMax;
239 Teb.StackReserved = InitialTeb->StackReserved;
240 }
241
242
243 /* more initialization */
244 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
245 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
246
247 /* write TEB data into teb page */
248 Status = NtWriteVirtualMemory(ProcessHandle,
249 TebBase,
250 &Teb,
251 sizeof(NT_TEB),
252 &ByteCount);
253
254 if (!NT_SUCCESS(Status))
255 {
256 /* free TEB */
257 DPRINT ("Writing TEB failed!\n");
258
259 RegionSize = 0;
260 NtFreeVirtualMemory(ProcessHandle,
261 TebBase,
262 &RegionSize,
263 MEM_RELEASE);
264
265 return Status;
266 }
267
268 /* FIXME: fs:[0] = TEB */
269
270 if (TebPtr != NULL)
271 {
272 // *TebPtr = (PNT_TEB)TebBase;
273 }
274
275 DPRINT ("TEB allocated at %p\n", TebBase);
276
277 return Status;
278 }
279
280
281 NTSTATUS STDCALL NtCreateThread (PHANDLE ThreadHandle,
282 ACCESS_MASK DesiredAccess,
283 POBJECT_ATTRIBUTES ObjectAttributes,
284 HANDLE ProcessHandle,
285 PCLIENT_ID Client,
286 PCONTEXT ThreadContext,
287 PINITIAL_TEB InitialTeb,
288 BOOLEAN CreateSuspended)
289 {
290 PETHREAD Thread;
291 PNT_TEB TebBase;
292 NTSTATUS Status;
293
294 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
295 ThreadHandle,ThreadContext);
296
297 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
298 DesiredAccess,ObjectAttributes);
299 if (!NT_SUCCESS(Status))
300 {
301 return(Status);
302 }
303
304 Status = HalInitTaskWithContext(Thread,ThreadContext);
305 if (!NT_SUCCESS(Status))
306 {
307 return(Status);
308 }
309
310 Status = PsCreateTeb (ProcessHandle,
311 &TebBase,
312 Thread,
313 InitialTeb);
314 if (!NT_SUCCESS(Status))
315 {
316 return(Status);
317 }
318
319 /* Attention: TebBase is in user memory space */
320 // Thread->Tcb.Teb = TebBase;
321
322 Thread->StartAddress=NULL;
323
324 if (Client!=NULL)
325 {
326 *Client=Thread->Cid;
327 }
328
329 if (!CreateSuspended)
330 {
331 DPRINT("Not creating suspended\n");
332 PsUnfreezeThread(Thread, NULL);
333 }
334 DPRINT("Thread %x\n", Thread);
335 DPRINT("ObGetReferenceCount(Thread) %d ObGetHandleCount(Thread) %x\n",
336 ObGetReferenceCount(Thread), ObGetHandleCount(Thread));
337 DPRINT("Finished PsCreateThread()\n");
338 return(STATUS_SUCCESS);
339 }
340
341
342 NTSTATUS PsCreateSystemThread(PHANDLE ThreadHandle,
343 ACCESS_MASK DesiredAccess,
344 POBJECT_ATTRIBUTES ObjectAttributes,
345 HANDLE ProcessHandle,
346 PCLIENT_ID ClientId,
347 PKSTART_ROUTINE StartRoutine,
348 PVOID StartContext)
349 /*
350 * FUNCTION: Creates a thread which executes in kernel mode
351 * ARGUMENTS:
352 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
353 * handle
354 * DesiredAccess = Requested access to the thread
355 * ObjectAttributes = Object attributes (optional)
356 * ProcessHandle = Handle of process thread will run in
357 * NULL to use system process
358 * ClientId (OUT) = Caller supplied storage for the returned client id
359 * of the thread (optional)
360 * StartRoutine = Entry point for the thread
361 * StartContext = Argument supplied to the thread when it begins
362 * execution
363 * RETURNS: Success or failure status
364 */
365 {
366 PETHREAD Thread;
367 NTSTATUS Status;
368
369 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
370 ThreadHandle,ProcessHandle);
371
372 Status = PsInitializeThread(ProcessHandle,&Thread,ThreadHandle,
373 DesiredAccess,ObjectAttributes);
374 if (!NT_SUCCESS(Status))
375 {
376 return(Status);
377 }
378
379 Thread->StartAddress=StartRoutine;
380 Status = HalInitTask(Thread,StartRoutine,StartContext);
381 if (!NT_SUCCESS(Status))
382 {
383 return(Status);
384 }
385
386 if (ClientId!=NULL)
387 {
388 *ClientId=Thread->Cid;
389 }
390
391 PsUnfreezeThread(Thread, NULL);
392
393 return(STATUS_SUCCESS);
394 }
395