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)
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
16 * All of the routines that manipulate the thread queue synchronize on
21 /* INCLUDES ****************************************************************/
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/pool.h>
29 #include <ntos/minmax.h>
30 #include <internal/ldr.h>
33 #include <internal/debug.h>
35 /* TYPES *******************************************************************/
37 typedef struct _NTW32CALL_SAVED_STATE
39 ULONG SavedStackLimit;
41 PVOID SavedInitialStack;
43 PULONG CallerResultLength;
44 PNTSTATUS CallbackStatus;
45 PKTRAP_FRAME SavedTrapFrame;
46 PVOID SavedCallbackStack;
47 } NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE;
53 } NTW32CALL_CALLBACK_STACK, *PNTW32CALL_CALLBACK_STACK;
55 static LIST_ENTRY CallbackStackListHead;
57 /* FUNCTIONS ***************************************************************/
60 PsInitialiseW32Call(VOID)
62 InitializeListHead(&CallbackStackListHead);
66 NtCallbackReturn (PVOID Result,
72 PNTSTATUS CallbackStatus;
73 PULONG CallerResultLength;
79 PNTW32CALL_SAVED_STATE State;
80 PKTRAP_FRAME SavedTrapFrame;
81 PVOID SavedCallbackStack;
83 Thread = PsGetCurrentThread();
84 if (Thread->Tcb.CallbackStack == NULL)
86 return(STATUS_NO_CALLBACK_ACTIVE);
89 OldStack = (PULONG)Thread->Tcb.CallbackStack;
92 * Get the values that NtW32Call left on the inactive stack for us.
94 State = (PNTW32CALL_SAVED_STATE)OldStack[0];
95 CallbackStatus = State->CallbackStatus;
96 CallerResultLength = State->CallerResultLength;
97 CallerResult = State->CallerResult;
98 InitialStack = State->SavedInitialStack;
99 StackBase = State->SavedStackBase;
100 StackLimit = State->SavedStackLimit;
101 SavedTrapFrame = State->SavedTrapFrame;
102 SavedCallbackStack = State->SavedCallbackStack;
105 * Copy the callback status and the callback result to NtW32Call
107 *CallbackStatus = Status;
108 if (CallerResult != NULL && CallerResultLength != NULL)
112 *CallerResultLength = 0;
116 *CallerResultLength = min(ResultLength, *CallerResultLength);
117 memcpy(*CallerResult, Result, *CallerResultLength);
122 * Restore the old stack.
124 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
125 Thread->Tcb.InitialStack = InitialStack;
126 Thread->Tcb.StackBase = StackBase;
127 Thread->Tcb.StackLimit = StackLimit;
128 Thread->Tcb.TrapFrame = SavedTrapFrame;
129 Thread->Tcb.CallbackStack = SavedCallbackStack;
130 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
131 KeStackSwitchAndRet((PVOID)(OldStack + 1));
133 /* Should never return. */
135 return(STATUS_UNSUCCESSFUL);
139 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
140 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry,
143 assert(SwapEntry == 0);
144 if (PhysAddr.QuadPart != 0)
146 MmReleasePageMemoryConsumer(MC_NPPOOL, PhysAddr);
151 PsFreeCallbackStack(PVOID StackLimit)
153 MmLockAddressSpace(MmGetKernelAddressSpace());
154 MmFreeMemoryArea(MmGetKernelAddressSpace(),
157 PsFreeCallbackStackPage,
159 MmUnlockAddressSpace(MmGetKernelAddressSpace());
163 PsFreeCallbackStacks(VOID)
165 PLIST_ENTRY CurrentListEntry;
166 PNTW32CALL_CALLBACK_STACK Current;
168 while (!IsListEmpty(&CallbackStackListHead))
170 CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
171 Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
173 PsFreeCallbackStack(Current->BaseAddress);
179 PsAllocateCallbackStack(ULONG StackSize)
181 PVOID KernelStack = NULL;
183 PMEMORY_AREA StackArea;
186 StackSize = PAGE_ROUND_UP(StackSize);
187 MmLockAddressSpace(MmGetKernelAddressSpace());
188 Status = MmCreateMemoryArea(NULL,
189 MmGetKernelAddressSpace(),
190 MEMORY_AREA_KERNEL_STACK,
197 MmUnlockAddressSpace(MmGetKernelAddressSpace());
198 if (!NT_SUCCESS(Status))
200 DPRINT("Failed to create thread stack\n");
203 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
205 PHYSICAL_ADDRESS Page;
206 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
207 if (!NT_SUCCESS(Status))
211 Status = MmCreateVirtualMapping(NULL,
212 KernelStack + (i * PAGE_SIZE),
213 PAGE_EXECUTE_READWRITE,
221 NtW32Call (IN ULONG RoutineIndex,
223 IN ULONG ArgumentLength,
224 OUT PVOID* Result OPTIONAL,
225 OUT PULONG ResultLength OPTIONAL)
230 PKTRAP_FRAME NewFrame;
233 NTSTATUS CallbackStatus;
234 NTW32CALL_SAVED_STATE SavedState;
235 PNTW32CALL_CALLBACK_STACK AssignedStack;
237 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
238 RoutineIndex, Argument, ArgumentLength);
240 Thread = PsGetCurrentThread();
242 /* Set up the new kernel and user environment. */
243 StackSize = (ULONG)(Thread->Tcb.StackBase - Thread->Tcb.StackLimit);
244 if (IsListEmpty(&CallbackStackListHead))
246 NewStack = PsAllocateCallbackStack(StackSize);
247 AssignedStack = ExAllocatePool(NonPagedPool,
248 sizeof(NTW32CALL_CALLBACK_STACK));
249 AssignedStack->BaseAddress = NewStack;
253 PLIST_ENTRY StackEntry;
255 StackEntry = RemoveHeadList(&CallbackStackListHead);
256 AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK,
258 NewStack = AssignedStack->BaseAddress;
260 /* FIXME: Need to check whether we were interrupted from v86 mode. */
261 memcpy(NewStack + StackSize - sizeof(KTRAP_FRAME), Thread->Tcb.TrapFrame,
262 sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
263 NewFrame = (PKTRAP_FRAME)(NewStack + StackSize - sizeof(KTRAP_FRAME));
264 NewFrame->Esp -= (ArgumentLength + (4 * sizeof(ULONG)));
265 NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
266 UserEsp = (PULONG)NewFrame->Esp;
267 UserEsp[0] = 0; /* Return address. */
268 UserEsp[1] = RoutineIndex;
269 UserEsp[2] = (ULONG)&UserEsp[4];
270 UserEsp[3] = ArgumentLength;
271 memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength);
273 /* Switch to the new environment and return to user-mode. */
274 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
275 SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
276 SavedState.SavedStackBase = Thread->Tcb.StackBase;
277 SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
278 SavedState.CallerResult = Result;
279 SavedState.CallerResultLength = ResultLength;
280 SavedState.CallbackStatus = &CallbackStatus;
281 SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
282 SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
283 Thread->Tcb.InitialStack = Thread->Tcb.StackBase = NewStack + StackSize;
284 Thread->Tcb.StackLimit = (ULONG)NewStack;
285 Thread->Tcb.KernelStack = NewStack + StackSize - sizeof(KTRAP_FRAME);
286 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
287 KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
290 * The callback return will have already restored most of the state we
293 KeLowerIrql(PASSIVE_LEVEL);
294 InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
295 return(CallbackStatus);