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;
49 /* FUNCTIONS ***************************************************************/
52 NtCallbackReturn (PVOID Result,
58 PNTSTATUS CallbackStatus;
59 PULONG CallerResultLength;
65 PNTW32CALL_SAVED_STATE State;
66 PKTRAP_FRAME SavedTrapFrame;
67 PVOID SavedCallbackStack;
69 Thread = PsGetCurrentThread();
70 if (Thread->Tcb.CallbackStack == NULL)
72 return(STATUS_NO_CALLBACK_ACTIVE);
75 OldStack = (PULONG)Thread->Tcb.CallbackStack;
78 * Get the values that NtW32Call left on the inactive stack for us.
80 State = (PNTW32CALL_SAVED_STATE)OldStack[0];
81 CallbackStatus = State->CallbackStatus;
82 CallerResultLength = State->CallerResultLength;
83 CallerResult = State->CallerResult;
84 InitialStack = State->SavedInitialStack;
85 StackBase = State->SavedStackBase;
86 StackLimit = State->SavedStackLimit;
87 SavedTrapFrame = State->SavedTrapFrame;
88 SavedCallbackStack = State->SavedCallbackStack;
91 * Copy the callback status and the callback result to NtW32Call
93 *CallbackStatus = Status;
94 if (CallerResult != NULL && CallerResultLength != NULL)
98 *CallerResultLength = 0;
102 *CallerResultLength = min(ResultLength, *CallerResultLength);
103 memcpy(*CallerResult, Result, *CallerResultLength);
108 * Restore the old stack.
110 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
111 Thread->Tcb.InitialStack = InitialStack;
112 Thread->Tcb.StackBase = StackBase;
113 Thread->Tcb.StackLimit = StackLimit;
114 Thread->Tcb.TrapFrame = SavedTrapFrame;
115 Thread->Tcb.CallbackStack = SavedCallbackStack;
116 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
117 KeStackSwitchAndRet((PVOID)(OldStack + 1));
119 /* Should never return. */
121 return(STATUS_UNSUCCESSFUL);
125 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
126 PHYSICAL_ADDRESS PhysAddr, SWAPENTRY SwapEntry,
129 assert(SwapEntry == 0);
130 if (PhysAddr.QuadPart != 0)
132 MmReleasePageMemoryConsumer(MC_NPPOOL, PhysAddr);
137 PsFreeCallbackStack(PVOID StackLimit)
139 MmLockAddressSpace(MmGetKernelAddressSpace());
140 MmFreeMemoryArea(MmGetKernelAddressSpace(),
143 PsFreeCallbackStackPage,
145 MmUnlockAddressSpace(MmGetKernelAddressSpace());
149 PsAllocateCallbackStack(ULONG StackSize)
151 PVOID KernelStack = NULL;
153 PMEMORY_AREA StackArea;
156 StackSize = PAGE_ROUND_UP(StackSize);
157 MmLockAddressSpace(MmGetKernelAddressSpace());
158 Status = MmCreateMemoryArea(NULL,
159 MmGetKernelAddressSpace(),
160 MEMORY_AREA_KERNEL_STACK,
166 MmUnlockAddressSpace(MmGetKernelAddressSpace());
167 if (!NT_SUCCESS(Status))
169 DPRINT("Failed to create thread stack\n");
172 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
174 PHYSICAL_ADDRESS Page;
175 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Page);
176 if (!NT_SUCCESS(Status))
180 Status = MmCreateVirtualMapping(NULL,
181 KernelStack + (i * PAGE_SIZE),
182 PAGE_EXECUTE_READWRITE,
190 NtW32Call (IN ULONG RoutineIndex,
192 IN ULONG ArgumentLength,
193 OUT PVOID* Result OPTIONAL,
194 OUT PULONG ResultLength OPTIONAL)
199 PKTRAP_FRAME NewFrame;
202 NTSTATUS CallbackStatus;
203 NTW32CALL_SAVED_STATE SavedState;
205 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
206 RoutineIndex, Argument, ArgumentLength);
208 Thread = PsGetCurrentThread();
210 /* Set up the new kernel and user environment. */
211 StackSize = (ULONG)(Thread->Tcb.StackBase - Thread->Tcb.StackLimit);
212 NewStack = PsAllocateCallbackStack(StackSize);
213 /* FIXME: Need to check whether we were interrupted from v86 mode. */
214 memcpy(NewStack + StackSize - sizeof(KTRAP_FRAME), Thread->Tcb.TrapFrame,
215 sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
216 NewFrame = (PKTRAP_FRAME)(NewStack + StackSize - sizeof(KTRAP_FRAME));
217 NewFrame->Esp -= (ArgumentLength + (4 * sizeof(ULONG)));
218 NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
219 UserEsp = (PULONG)NewFrame->Esp;
220 UserEsp[0] = 0; /* Return address. */
221 UserEsp[1] = RoutineIndex;
222 UserEsp[2] = (ULONG)&UserEsp[4];
223 UserEsp[3] = ArgumentLength;
224 memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength);
226 /* Switch to the new environment and return to user-mode. */
227 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
228 SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
229 SavedState.SavedStackBase = Thread->Tcb.StackBase;
230 SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
231 SavedState.CallerResult = Result;
232 SavedState.CallerResultLength = ResultLength;
233 SavedState.CallbackStatus = &CallbackStatus;
234 SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
235 SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
236 Thread->Tcb.InitialStack = Thread->Tcb.StackBase = NewStack + StackSize;
237 Thread->Tcb.StackLimit = (ULONG)NewStack;
238 Thread->Tcb.KernelStack = NewStack + StackSize - sizeof(KTRAP_FRAME);
239 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack;
240 KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
243 * The callback return will have already restored most of the state we
246 KeLowerIrql(PASSIVE_LEVEL);
247 PsFreeCallbackStack(NewStack);
248 return(CallbackStatus);