bac75e241badfa0a21b8c020967febf796de83fa
[reactos.git] / reactos / ntoskrnl / ke / kthread.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2000 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * FILE: ntoskrnl/ke/kthread.c
22 * PURPOSE: Microkernel thread support
23 * PROGRAMMER: David Welch (welch@cwcom.net)
24 * UPDATE HISTORY:
25 * Created 22/05/98
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ntoskrnl.h>
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* FUNCTIONS *****************************************************************/
35
36 VOID
37 KiServiceCheck (VOID)
38 {
39 PETHREAD Thread;
40
41 Thread = PsGetCurrentThread();
42
43 if (Thread->Tcb.ServiceTable != KeServiceDescriptorTableShadow)
44 {
45 PsInitWin32Thread (Thread);
46
47 Thread->Tcb.ServiceTable = KeServiceDescriptorTableShadow;
48 }
49 }
50
51 /*
52 * @unimplemented
53 */
54 VOID
55 STDCALL
56 KeCapturePersistentThreadState(
57 IN PVOID CurrentThread,
58 IN ULONG Setting1,
59 IN ULONG Setting2,
60 IN ULONG Setting3,
61 IN ULONG Setting4,
62 IN ULONG Setting5,
63 IN PVOID ThreadState
64 )
65 {
66 UNIMPLEMENTED;
67 }
68
69 VOID
70 KeFreeStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
71 PFN_TYPE Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
72 {
73 ASSERT(SwapEntry == 0);
74 if (Page != 0)
75 {
76 MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
77 }
78 }
79
80 /*
81 * @implemented
82 */
83 KPRIORITY
84 STDCALL
85 KeQueryPriorityThread (
86 IN PKTHREAD Thread
87 )
88 {
89 return Thread->Priority;
90 }
91
92 NTSTATUS
93 KeReleaseThread(PKTHREAD Thread)
94 /*
95 * FUNCTION: Releases the resource allocated for a thread by
96 * KeInitializeThread
97 * NOTE: The thread had better not be running when this is called
98 */
99 {
100 extern unsigned int init_stack;
101
102 /* FIXME - lock the process */
103 RemoveEntryList(&Thread->ThreadListEntry);
104
105 if (Thread->StackLimit != (ULONG_PTR)&init_stack)
106 {
107 MmLockAddressSpace(MmGetKernelAddressSpace());
108 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
109 (PVOID)Thread->StackLimit,
110 KeFreeStackPage,
111 NULL);
112 MmUnlockAddressSpace(MmGetKernelAddressSpace());
113 }
114 Thread->StackLimit = 0;
115 Thread->InitialStack = NULL;
116 Thread->StackBase = NULL;
117 Thread->KernelStack = NULL;
118 return(STATUS_SUCCESS);
119 }
120
121 /*
122 * @implemented
123 */
124 BOOLEAN
125 STDCALL
126 KeSetKernelStackSwapEnable(
127 IN BOOLEAN Enable
128 )
129 {
130 PKTHREAD Thread;
131 BOOLEAN PreviousState;
132
133 Thread = KeGetCurrentThread();
134
135 /* Save Old State */
136 PreviousState = Thread->EnableStackSwap;
137
138 /* Set New State */
139 Thread->EnableStackSwap = Enable;
140
141 /* Return Old State */
142 return PreviousState;
143 }
144
145 VOID
146 KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First)
147 /*
148 * FUNCTION: Initialize the microkernel state of the thread
149 */
150 {
151 PVOID KernelStack;
152 NTSTATUS Status;
153 extern unsigned int init_stack_top;
154 extern unsigned int init_stack;
155 PMEMORY_AREA StackArea;
156 ULONG i;
157 PHYSICAL_ADDRESS BoundaryAddressMultiple;
158
159 BoundaryAddressMultiple.QuadPart = 0;
160
161 KeInitializeDispatcherHeader(&Thread->DispatcherHeader,
162 InternalThreadType,
163 sizeof(ETHREAD),
164 FALSE);
165 InitializeListHead(&Thread->MutantListHead);
166 if (!First)
167 {
168 PFN_TYPE Page[MM_STACK_SIZE / PAGE_SIZE];
169 KernelStack = NULL;
170
171 MmLockAddressSpace(MmGetKernelAddressSpace());
172 Status = MmCreateMemoryArea(NULL,
173 MmGetKernelAddressSpace(),
174 MEMORY_AREA_KERNEL_STACK,
175 &KernelStack,
176 MM_STACK_SIZE,
177 0,
178 &StackArea,
179 FALSE,
180 FALSE,
181 BoundaryAddressMultiple);
182 MmUnlockAddressSpace(MmGetKernelAddressSpace());
183
184 if (!NT_SUCCESS(Status))
185 {
186 DPRINT1("Failed to create thread stack\n");
187 KEBUGCHECK(0);
188 }
189 for (i = 0; i < (MM_STACK_SIZE / PAGE_SIZE); i++)
190 {
191 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page[i]);
192 if (!NT_SUCCESS(Status))
193 {
194 KEBUGCHECK(0);
195 }
196 }
197 Status = MmCreateVirtualMapping(NULL,
198 KernelStack,
199 PAGE_READWRITE,
200 Page,
201 MM_STACK_SIZE / PAGE_SIZE);
202 if (!NT_SUCCESS(Status))
203 {
204 KEBUGCHECK(0);
205 }
206 Thread->InitialStack = (PCHAR)KernelStack + MM_STACK_SIZE;
207 Thread->StackBase = (PCHAR)KernelStack + MM_STACK_SIZE;
208 Thread->StackLimit = (ULONG_PTR)KernelStack;
209 Thread->KernelStack = (PCHAR)KernelStack + MM_STACK_SIZE;
210 }
211 else
212 {
213 Thread->InitialStack = (PCHAR)&init_stack_top;
214 Thread->StackBase = (PCHAR)&init_stack_top;
215 Thread->StackLimit = (ULONG_PTR)&init_stack;
216 Thread->KernelStack = (PCHAR)&init_stack_top;
217 }
218
219 /*
220 * Establish the pde's for the new stack and the thread structure within the
221 * address space of the new process. They are accessed while taskswitching or
222 * while handling page faults. At this point it isn't possible to call the
223 * page fault handler for the missing pde's.
224 */
225
226 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
227 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
228
229 /*
230 * The Native API function will initialize the TEB field later
231 */
232 Thread->Teb = NULL;
233 Thread->TlsArray = NULL;
234 Thread->DebugActive = 0;
235 Thread->State = THREAD_STATE_INITIALIZED;
236 Thread->Alerted[0] = 0;
237 Thread->Alerted[1] = 0;
238 Thread->Iopl = 0;
239 /*
240 * FIXME: Think how this might work
241 */
242 Thread->NpxState = 0;
243
244 Thread->Saturation = 0;
245 Thread->Priority = Process->BasePriority;
246 InitializeListHead(&Thread->ApcState.ApcListHead[0]);
247 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
248 Thread->ApcState.Process = Process;
249 Thread->ApcState.KernelApcInProgress = 0;
250 Thread->ApcState.KernelApcPending = 0;
251 Thread->ApcState.UserApcPending = 0;
252 Thread->ContextSwitches = 0;
253 Thread->WaitStatus = STATUS_SUCCESS;
254 Thread->WaitIrql = PASSIVE_LEVEL;
255 Thread->WaitMode = 0;
256 Thread->WaitNext = FALSE;
257 Thread->WaitBlockList = NULL;
258 Thread->WaitListEntry.Flink = NULL;
259 Thread->WaitListEntry.Blink = NULL;
260 Thread->WaitTime = 0;
261 Thread->BasePriority = Process->BasePriority;
262 Thread->DecrementCount = 0;
263 Thread->PriorityDecrement = 0;
264 Thread->Quantum = Process->ThreadQuantum;
265 memset(Thread->WaitBlock, 0, sizeof(KWAIT_BLOCK)*4);
266 Thread->LegoData = 0;
267 Thread->UserAffinity = Process->Affinity;
268 Thread->SystemAffinityActive = 0;
269 Thread->PowerState = 0;
270 Thread->NpxIrql = 0;
271 Thread->ServiceTable = KeServiceDescriptorTable;
272 Thread->Queue = NULL;
273 KeInitializeSpinLock(&Thread->ApcQueueLock);
274 memset(&Thread->Timer, 0, sizeof(KTIMER));
275 KeInitializeTimer(&Thread->Timer);
276 Thread->QueueListEntry.Flink = NULL;
277 Thread->QueueListEntry.Blink = NULL;
278 Thread->Affinity = Process->Affinity;
279 Thread->Preempted = 0;
280 Thread->ProcessReadyQueue = 0;
281 Thread->KernelStackResident = 1;
282 Thread->NextProcessor = 0;
283 Thread->CallbackStack = NULL;
284 Thread->Win32Thread = NULL;
285 Thread->TrapFrame = NULL;
286 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
287 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
288 Thread->EnableStackSwap = 0;
289 Thread->LargeStack = 0;
290 Thread->ResourceIndex = 0;
291 Thread->PreviousMode = KernelMode;
292 Thread->KernelTime = 0;
293 Thread->UserTime = 0;
294 memset(&Thread->SavedApcState, 0, sizeof(KAPC_STATE));
295
296 Thread->ApcStateIndex = OriginalApcEnvironment;
297 Thread->ApcQueueable = TRUE;
298 Thread->AutoAlignment = Process->AutoAlignment;
299
300 KeInitializeApc(&Thread->SuspendApc,
301 Thread,
302 OriginalApcEnvironment,
303 PiSuspendThreadKernelRoutine,
304 PiSuspendThreadRundownRoutine,
305 PiSuspendThreadNormalRoutine,
306 KernelMode,
307 NULL);
308 KeInitializeSemaphore(&Thread->SuspendSemaphore, 0, 128);
309
310 InsertTailList(&Process->ThreadListHead,
311 &Thread->ThreadListEntry);
312 Thread->FreezeCount = 0;
313 Thread->SuspendCount = 0;
314
315 /*
316 * Do x86 specific part
317 */
318 }
319
320 /*
321 * @implemented
322 */
323 VOID
324 STDCALL
325 KeRevertToUserAffinityThread(VOID)
326 {
327 #ifdef CONFIG_SMP
328 PKTHREAD CurrentThread;
329 KIRQL oldIrql;
330
331 oldIrql = KeAcquireDispatcherDatabaseLock();
332
333 CurrentThread = KeGetCurrentThread();
334
335 ASSERT(CurrentThread->SystemAffinityActive != FALSE);
336
337 /* Return to User Affinity */
338 CurrentThread->Affinity = CurrentThread->UserAffinity;
339
340 /* Disable System Affinity */
341 CurrentThread->SystemAffinityActive = FALSE;
342
343 if (CurrentThread->Affinity & (1 << KeGetCurrentProcessorNumber()))
344 {
345 KeReleaseDispatcherDatabaseLock(oldIrql);
346 }
347 else
348 {
349 CurrentThread->WaitIrql = oldIrql;
350 PsDispatchThreadNoLock(THREAD_STATE_READY);
351 KeLowerIrql(oldIrql);
352 }
353 #endif
354 }
355
356 /*
357 * @implemented
358 */
359 CCHAR
360 STDCALL
361 KeSetIdealProcessorThread (
362 IN PKTHREAD Thread,
363 IN CCHAR Processor)
364 {
365 CCHAR PreviousIdealProcessor;
366
367 /* Save Old Ideal Processor */
368 PreviousIdealProcessor = Thread->IdealProcessor;
369
370 /* Set New Ideal Processor */
371 Thread->IdealProcessor = Processor;
372
373 /* Return Old Ideal Processor */
374 return PreviousIdealProcessor;
375 }
376
377 /*
378 * @implemented
379 */
380 VOID
381 STDCALL
382 KeSetSystemAffinityThread(IN KAFFINITY Affinity)
383 {
384 #ifdef CONFIG_SMP
385 PKTHREAD CurrentThread;
386 KIRQL oldIrql;
387
388 oldIrql = KeAcquireDispatcherDatabaseLock();
389
390 CurrentThread = KeGetCurrentThread();
391
392 ASSERT(Affinity & ((1 << KeNumberProcessors) - 1));
393
394 /* Set the System Affinity Specified */
395 CurrentThread->Affinity = Affinity;
396
397 /* Enable System Affinity */
398 CurrentThread->SystemAffinityActive = TRUE;
399
400 if (Affinity & (1 << KeGetCurrentProcessorNumber()))
401 {
402 KeReleaseDispatcherDatabaseLock(oldIrql);
403 }
404 else
405 {
406 CurrentThread->WaitIrql = oldIrql;
407 PsDispatchThreadNoLock(THREAD_STATE_READY);
408 KeLowerIrql(oldIrql);
409 }
410 #endif
411 }
412
413 /*
414 * @implemented
415 */
416 VOID
417 STDCALL
418 KeTerminateThread(IN KPRIORITY Increment)
419 {
420 /* The Increment Argument seems to be ignored by NT and always 0 when called */
421
422 /* Call our own internal routine */
423 PsTerminateCurrentThread(0);
424 }