6c699aa53b6b3da732d6841cab4cc6f6670ec466
[reactos.git] / reactos / lib / rtl / thread.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Rtl user thread functions
5 * FILE: lib/rtl/thread.c
6 * PROGRAMERS:
7 * Alex Ionescu (alex@relsoft.net)
8 * Eric Kohl
9 * KJK::Hyperion
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <rtl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PRIVATE FUNCTIONS *******************************************************/
20
21 NTSTATUS
22 NTAPI
23 RtlpCreateUserStack(HANDLE hProcess,
24 ULONG StackReserve,
25 ULONG StackCommit,
26 ULONG StackZeroBits,
27 PINITIAL_TEB InitialTeb)
28 {
29 NTSTATUS Status;
30 SYSTEM_BASIC_INFORMATION SystemBasicInfo;
31 PIMAGE_NT_HEADERS Headers;
32 ULONG_PTR Stack = 0;
33 BOOLEAN UseGuard = FALSE;
34
35 DPRINT("RtlpCreateUserStack\n");
36
37 /* Get some memory information */
38 Status = NtQuerySystemInformation(SystemBasicInformation,
39 &SystemBasicInfo,
40 sizeof(SYSTEM_BASIC_INFORMATION),
41 NULL);
42 if (!NT_SUCCESS(Status))
43 {
44 DPRINT1("Failure to query system info\n");
45 return Status;
46 }
47
48 /* Use the Image Settings if we are dealing with the current Process */
49 if (hProcess == NtCurrentProcess())
50 {
51 /* Get the Image Headers */
52 Headers = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
53 DPRINT("Headers: %p\n", Headers);
54
55 /* If we didn't get the parameters, find them ourselves */
56 StackReserve = (StackReserve) ?
57 StackReserve : Headers->OptionalHeader.SizeOfStackReserve;
58 StackCommit = (StackCommit) ?
59 StackCommit : Headers->OptionalHeader.SizeOfStackCommit;
60 }
61 else
62 {
63 /* Use the System Settings if needed */
64 StackReserve = (StackReserve) ? StackReserve :
65 SystemBasicInfo.AllocationGranularity;
66 StackCommit = (StackCommit) ? StackCommit : SystemBasicInfo.PageSize;
67 }
68
69 /* Align everything to Page Size */
70 StackReserve = ROUND_UP(StackReserve, SystemBasicInfo.AllocationGranularity);
71 StackCommit = ROUND_UP(StackCommit, SystemBasicInfo.PageSize);
72 #if 1 // FIXME: Remove once Guard Page support is here
73 StackCommit = StackReserve;
74 #endif
75 DPRINT("StackReserve: %lx, StackCommit: %lx\n", StackReserve, StackCommit);
76
77 /* Reserve memory for the stack */
78 Status = ZwAllocateVirtualMemory(hProcess,
79 (PVOID*)&Stack,
80 StackZeroBits,
81 &StackReserve,
82 MEM_RESERVE,
83 PAGE_READWRITE);
84 if (!NT_SUCCESS(Status))
85 {
86 DPRINT1("Failure to reserve stack\n");
87 return Status;
88 }
89
90 /* Now set up some basic Initial TEB Parameters */
91 InitialTeb->PreviousStackBase = NULL;
92 InitialTeb->PreviousStackLimit = NULL;
93 InitialTeb->AllocatedStackBase = (PVOID)Stack;
94 InitialTeb->StackBase = (PVOID)(Stack + StackReserve);
95
96 /* Update the Stack Position */
97 Stack += StackReserve - StackCommit;
98
99 /* Check if we will need a guard page */
100 if (StackReserve > StackCommit)
101 {
102 Stack -= SystemBasicInfo.PageSize;
103 StackCommit += SystemBasicInfo.PageSize;
104 UseGuard = TRUE;
105 }
106
107 DPRINT("AllocatedBase: %p, StackBase: %p, Stack: %lx, StackCommit: %lx\n",
108 InitialTeb->AllocatedStackBase, InitialTeb->StackBase, Stack,
109 StackCommit);
110
111 /* Allocate memory for the stack */
112 Status = ZwAllocateVirtualMemory(hProcess,
113 (PVOID*)&Stack,
114 0,
115 &StackCommit,
116 MEM_COMMIT,
117 PAGE_READWRITE);
118 if (!NT_SUCCESS(Status))
119 {
120 DPRINT1("Failure to allocate stack\n");
121 return Status;
122 }
123
124 /* Now set the current Stack Limit */
125 InitialTeb->StackLimit = (PVOID)Stack;
126 DPRINT("StackLimit: %lx\n", Stack);
127
128 /* Create a guard page */
129 if (UseGuard)
130 {
131 ULONG GuardPageSize = SystemBasicInfo.PageSize;
132 ULONG Dummy;
133
134 /* Attempt maximum space possible */
135 Status = ZwProtectVirtualMemory(hProcess,
136 (PVOID*)&Stack,
137 &GuardPageSize,
138 PAGE_GUARD | PAGE_READWRITE,
139 &Dummy);
140 if (!NT_SUCCESS(Status))
141 {
142 DPRINT1("Failure to create guard page\n");
143 return Status;
144 }
145
146 /* Update the Stack Limit keeping in mind the Guard Page */
147 InitialTeb->StackLimit = (PVOID)((ULONG_PTR)InitialTeb->StackLimit -
148 GuardPageSize);
149 DPRINT1("StackLimit: %lx\n", Stack);
150 }
151
152 /* We are done! */
153 return Status;
154 }
155
156 NTSTATUS
157 NTAPI
158 RtlpFreeUserStack(HANDLE hProcess,
159 PINITIAL_TEB InitialTeb)
160 {
161 ULONG Dummy = 0;
162
163 /* Free the Stack */
164 return ZwFreeVirtualMemory(hProcess,
165 &InitialTeb->AllocatedStackBase,
166 &Dummy,
167 MEM_RELEASE);
168 }
169
170 /* FUNCTIONS ***************************************************************/
171
172 /*
173 @implemented
174 */
175 NTSTATUS
176 NTAPI
177 RtlCreateUserThread(HANDLE ProcessHandle,
178 PSECURITY_DESCRIPTOR SecurityDescriptor,
179 BOOLEAN CreateSuspended,
180 LONG StackZeroBits,
181 ULONG StackReserve,
182 ULONG StackCommit,
183 PTHREAD_START_ROUTINE StartAddress,
184 PVOID Parameter,
185 PHANDLE ThreadHandle,
186 PCLIENT_ID ClientId)
187 {
188 NTSTATUS Status;
189 HANDLE Handle;
190 CLIENT_ID ThreadCid;
191 INITIAL_TEB InitialTeb;
192 OBJECT_ATTRIBUTES ObjectAttributes;
193 CONTEXT Context;
194
195 DPRINT("RtlCreateUserThread: (hProcess: %p, Suspended: %d,"
196 "ZeroBits: %lx, StackReserve: %lx, StackCommit: %lx,"
197 "StartAddress: %p, Parameter: %p)\n", ProcessHandle,
198 CreateSuspended, StackZeroBits, StackReserve, StackCommit,
199 StartAddress, Parameter);
200
201 /* First, we'll create the Stack */
202 Status = RtlpCreateUserStack(ProcessHandle,
203 StackReserve,
204 StackCommit,
205 StackZeroBits,
206 &InitialTeb);
207 if (!NT_SUCCESS(Status))
208 {
209 DPRINT1("Failure to create User Stack\n");
210 return Status;
211 }
212
213 /* Next, we'll set up the Initial Context */
214 RtlInitializeContext(ProcessHandle,
215 &Context,
216 Parameter,
217 StartAddress,
218 InitialTeb.StackBase);
219
220 /* We are now ready to create the Kernel Thread Object */
221 InitializeObjectAttributes(&ObjectAttributes,
222 NULL,
223 0,
224 NULL,
225 SecurityDescriptor);
226 Status = ZwCreateThread(&Handle,
227 THREAD_ALL_ACCESS,
228 &ObjectAttributes,
229 ProcessHandle,
230 &ThreadCid,
231 &Context,
232 &InitialTeb,
233 CreateSuspended);
234 if (!NT_SUCCESS(Status))
235 {
236 DPRINT1("Failure to create Thread\n");
237
238 /* Free the stack */
239 RtlpFreeUserStack(ProcessHandle, &InitialTeb);
240 }
241 else
242 {
243 DPRINT("Thread created: %p\n", Handle);
244 if (ThreadHandle) *ThreadHandle = Handle;
245 if (ClientId) *ClientId = ThreadCid;
246 }
247
248 /* Return success or the previous failure */
249 return Status;
250 }
251
252 /*
253 * FIXME: Should go in /i386
254 @implemented
255 */
256 VOID
257 NTAPI
258 RtlInitializeContext(IN HANDLE ProcessHandle,
259 OUT PCONTEXT ThreadContext,
260 IN PVOID ThreadStartParam OPTIONAL,
261 IN PTHREAD_START_ROUTINE ThreadStartAddress,
262 IN PINITIAL_TEB InitialTeb)
263 {
264 DPRINT("RtlInitializeContext: (hProcess: %p, ThreadContext: %p, Teb: %p\n",
265 ProcessHandle, ThreadContext, InitialTeb);
266
267 /*
268 * Set the Initial Registers
269 * This is based on NT's default values -- crazy apps might expect this...
270 */
271 ThreadContext->Ebp = 0;
272 ThreadContext->Eax = 0;
273 ThreadContext->Ebx = 1;
274 ThreadContext->Ecx = 2;
275 ThreadContext->Edx = 3;
276 ThreadContext->Esi = 4;
277 ThreadContext->Edi = 5;
278
279 /* Set the Selectors */
280 ThreadContext->SegGs = 0;
281 ThreadContext->SegFs = KGDT_R3_TEB | RPL_MASK;
282 ThreadContext->SegEs = KGDT_R3_DATA | RPL_MASK;
283 ThreadContext->SegDs = KGDT_R3_DATA | RPL_MASK;
284 ThreadContext->SegCs = KGDT_R3_CODE | RPL_MASK;
285 ThreadContext->SegSs = KGDT_R3_DATA | RPL_MASK;
286
287 /* Enable Interrupts */
288 ThreadContext->EFlags = 0x200; /*X86_EFLAGS_IF */
289
290 /* Settings passed */
291 ThreadContext->Eip = (ULONG)ThreadStartAddress;
292 ThreadContext->Esp = (ULONG)InitialTeb;
293
294 /* Only the basic Context is initialized */
295 ThreadContext->ContextFlags = CONTEXT_CONTROL |
296 CONTEXT_INTEGER |
297 CONTEXT_SEGMENTS;
298
299 /* Set up ESP to the right value */
300 ThreadContext->Esp -= sizeof(PVOID);
301 ZwWriteVirtualMemory(ProcessHandle,
302 (PVOID)ThreadContext->Esp,
303 (PVOID)&ThreadStartParam,
304 sizeof(PVOID),
305 NULL);
306
307 /* Push it down one more notch for RETEIP */
308 ThreadContext->Esp -= sizeof(PVOID);
309 }
310
311 /*
312 * @implemented
313 */
314 VOID
315 NTAPI
316 RtlExitUserThread(NTSTATUS Status)
317 {
318 /* Call the Loader and tell him to notify the DLLs */
319 LdrShutdownThread();
320
321 /* Shut us down */
322 NtCurrentTeb()->FreeStackOnTermination = TRUE;
323 NtTerminateThread(NtCurrentThread(), Status);
324 }
325
326 /*
327 @implemented
328 */
329 VOID
330 NTAPI
331 RtlFreeUserThreadStack(HANDLE ProcessHandle,
332 HANDLE ThreadHandle)
333 {
334 NTSTATUS Status;
335 THREAD_BASIC_INFORMATION ThreadBasicInfo;
336 ULONG Dummy, Size = 0;
337 PVOID StackLocation;
338
339 /* Query the Basic Info */
340 Status = NtQueryInformationThread(ThreadHandle,
341 ThreadBasicInformation,
342 &ThreadBasicInfo,
343 sizeof(THREAD_BASIC_INFORMATION),
344 NULL);
345 if (!NT_SUCCESS(Status) || !ThreadBasicInfo.TebBaseAddress)
346 {
347 DPRINT1("Could not query info, or TEB is NULL\n");
348 return;
349 }
350
351 /* Get the deallocation stack */
352 Status = NtReadVirtualMemory(ProcessHandle,
353 &((PTEB)ThreadBasicInfo.TebBaseAddress)->
354 DeallocationStack,
355 &StackLocation,
356 sizeof(PVOID),
357 &Dummy);
358 if (!NT_SUCCESS(Status) || !StackLocation)
359 {
360 DPRINT1("Could not read Deallocation Base\n");
361 return;
362 }
363
364 /* Free it */
365 NtFreeVirtualMemory(ProcessHandle, &StackLocation, &Size, MEM_RELEASE);
366 }
367
368 PTEB
369 NTAPI
370 _NtCurrentTeb(VOID)
371 {
372 /* Return the TEB */
373 return NtCurrentTeb();
374 }
375
376 /* EOF */