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