Standardize comment headers. Patch by Trevor McCort
[reactos.git] / reactos / ntoskrnl / ps / w32call.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/w32call.c
6 * PURPOSE: Thread managment
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Phillip Susi
10 */
11
12 /*
13 * NOTE:
14 *
15 * All of the routines that manipulate the thread queue synchronize on
16 * a single spinlock
17 *
18 */
19
20 /* INCLUDES ****************************************************************/
21
22 #include <ntoskrnl.h>
23 #define NDEBUG
24 #include <internal/debug.h>
25
26 #if defined(__GNUC__)
27 /* void * alloca(size_t size); */
28 #elif defined(_MSC_VER)
29 void* _alloca(size_t size);
30 #else
31 #error Unknown compiler for alloca intrinsic stack allocation "function"
32 #endif
33
34 /* TYPES *******************************************************************/
35
36 typedef struct _NTW32CALL_SAVED_STATE
37 {
38 ULONG_PTR SavedStackLimit;
39 PVOID SavedStackBase;
40 PVOID SavedInitialStack;
41 PVOID CallerResult;
42 PULONG CallerResultLength;
43 PNTSTATUS CallbackStatus;
44 PKTRAP_FRAME SavedTrapFrame;
45 PVOID SavedCallbackStack;
46 PVOID SavedExceptionStack;
47 } NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE;
48
49 typedef struct
50 {
51 PVOID BaseAddress;
52 LIST_ENTRY ListEntry;
53 } NTW32CALL_CALLBACK_STACK, *PNTW32CALL_CALLBACK_STACK;
54
55 KSPIN_LOCK CallbackStackListLock;
56 static LIST_ENTRY CallbackStackListHead;
57
58 /* FUNCTIONS ***************************************************************/
59
60 VOID INIT_FUNCTION
61 PsInitialiseW32Call(VOID)
62 {
63 InitializeListHead(&CallbackStackListHead);
64 KeInitializeSpinLock(&CallbackStackListLock);
65 }
66
67 NTSTATUS STDCALL
68 NtCallbackReturn (PVOID Result,
69 ULONG ResultLength,
70 NTSTATUS Status)
71 {
72 PULONG OldStack;
73 PETHREAD Thread;
74 PNTSTATUS CallbackStatus;
75 PULONG CallerResultLength;
76 PVOID* CallerResult;
77 PVOID InitialStack;
78 PVOID StackBase;
79 ULONG_PTR StackLimit;
80 KIRQL oldIrql;
81 PNTW32CALL_SAVED_STATE State;
82 PKTRAP_FRAME SavedTrapFrame;
83 PVOID SavedCallbackStack;
84 PVOID SavedExceptionStack;
85
86 Thread = PsGetCurrentThread();
87 if (Thread->Tcb.CallbackStack == NULL)
88 {
89 return(STATUS_NO_CALLBACK_ACTIVE);
90 }
91
92 OldStack = (PULONG)Thread->Tcb.CallbackStack;
93
94 /*
95 * Get the values that NtW32Call left on the inactive stack for us.
96 */
97 State = (PNTW32CALL_SAVED_STATE)OldStack[0];
98 CallbackStatus = State->CallbackStatus;
99 CallerResultLength = State->CallerResultLength;
100 CallerResult = State->CallerResult;
101 InitialStack = State->SavedInitialStack;
102 StackBase = State->SavedStackBase;
103 StackLimit = State->SavedStackLimit;
104 SavedTrapFrame = State->SavedTrapFrame;
105 SavedCallbackStack = State->SavedCallbackStack;
106 SavedExceptionStack = State->SavedExceptionStack;
107
108 /*
109 * Copy the callback status and the callback result to NtW32Call
110 */
111 *CallbackStatus = Status;
112 if (CallerResult != NULL && CallerResultLength != NULL)
113 {
114 if (Result == NULL)
115 {
116 *CallerResultLength = 0;
117 }
118 else
119 {
120 *CallerResultLength = min(ResultLength, *CallerResultLength);
121 memcpy(*CallerResult, Result, *CallerResultLength);
122 }
123 }
124
125 /*
126 * Restore the old stack.
127 */
128 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
129 if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
130 ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread)
131 {
132 memcpy((char*)InitialStack - sizeof(FX_SAVE_AREA),
133 (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA),
134 sizeof(FX_SAVE_AREA));
135 }
136 Thread->Tcb.InitialStack = InitialStack;
137 Thread->Tcb.StackBase = StackBase;
138 Thread->Tcb.StackLimit = StackLimit;
139 Thread->Tcb.TrapFrame = SavedTrapFrame;
140 Thread->Tcb.CallbackStack = SavedCallbackStack;
141 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)SavedExceptionStack;
142 KeStackSwitchAndRet((PVOID)(OldStack + 1));
143
144 /* Should never return. */
145 KEBUGCHECK(0);
146 return(STATUS_UNSUCCESSFUL);
147 }
148
149 VOID STATIC
150 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
151 PFN_TYPE Page, SWAPENTRY SwapEntry,
152 BOOLEAN Dirty)
153 {
154 ASSERT(SwapEntry == 0);
155 if (Page != 0)
156 {
157 MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
158 }
159 }
160
161 VOID STATIC
162 PsFreeCallbackStack(PVOID StackLimit)
163 {
164 MmLockAddressSpace(MmGetKernelAddressSpace());
165 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
166 StackLimit,
167 PsFreeCallbackStackPage,
168 NULL);
169 MmUnlockAddressSpace(MmGetKernelAddressSpace());
170 }
171
172 VOID
173 PsFreeCallbackStacks(VOID)
174 {
175 PLIST_ENTRY CurrentListEntry;
176 PNTW32CALL_CALLBACK_STACK Current;
177
178 while (!IsListEmpty(&CallbackStackListHead))
179 {
180 CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
181 Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
182 ListEntry);
183 PsFreeCallbackStack(Current->BaseAddress);
184 ExFreePool(Current);
185 }
186 }
187
188 PVOID STATIC
189 PsAllocateCallbackStack(ULONG StackSize)
190 {
191 PVOID KernelStack = NULL;
192 NTSTATUS Status;
193 PMEMORY_AREA StackArea;
194 ULONG i, j;
195 PHYSICAL_ADDRESS BoundaryAddressMultiple;
196 PPFN_TYPE Pages = alloca(sizeof(PFN_TYPE) * (StackSize /PAGE_SIZE));
197
198
199 BoundaryAddressMultiple.QuadPart = 0;
200 StackSize = PAGE_ROUND_UP(StackSize);
201 MmLockAddressSpace(MmGetKernelAddressSpace());
202 Status = MmCreateMemoryArea(NULL,
203 MmGetKernelAddressSpace(),
204 MEMORY_AREA_KERNEL_STACK,
205 &KernelStack,
206 StackSize,
207 0,
208 &StackArea,
209 FALSE,
210 FALSE,
211 BoundaryAddressMultiple);
212 MmUnlockAddressSpace(MmGetKernelAddressSpace());
213 if (!NT_SUCCESS(Status))
214 {
215 DPRINT("Failed to create thread stack\n");
216 return(NULL);
217 }
218 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
219 {
220 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Pages[i]);
221 if (!NT_SUCCESS(Status))
222 {
223 for (j = 0; j < i; j++)
224 {
225 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[j]);
226 }
227 return(NULL);
228 }
229 }
230 Status = MmCreateVirtualMapping(NULL,
231 KernelStack,
232 PAGE_READWRITE,
233 Pages,
234 StackSize / PAGE_SIZE);
235 if (!NT_SUCCESS(Status))
236 {
237 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
238 {
239 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[i]);
240 }
241 return(NULL);
242 }
243 return(KernelStack);
244 }
245
246 NTSTATUS STDCALL
247 NtW32Call (IN ULONG RoutineIndex,
248 IN PVOID Argument,
249 IN ULONG ArgumentLength,
250 OUT PVOID* Result OPTIONAL,
251 OUT PULONG ResultLength OPTIONAL)
252 {
253 PETHREAD Thread;
254 PVOID NewStack;
255 ULONG_PTR StackSize;
256 PKTRAP_FRAME NewFrame;
257 PULONG UserEsp;
258 KIRQL oldIrql;
259 NTSTATUS CallbackStatus;
260 NTW32CALL_SAVED_STATE SavedState;
261 PNTW32CALL_CALLBACK_STACK AssignedStack;
262
263 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
264 RoutineIndex, Argument, ArgumentLength);
265
266 Thread = PsGetCurrentThread();
267
268 /* Set up the new kernel and user environment. */
269 StackSize = (ULONG_PTR)Thread->Tcb.StackBase - Thread->Tcb.StackLimit;
270 KeAcquireSpinLock(&CallbackStackListLock, &oldIrql);
271 if (IsListEmpty(&CallbackStackListHead))
272 {
273 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
274 NewStack = PsAllocateCallbackStack(StackSize);
275 AssignedStack = ExAllocatePool(NonPagedPool,
276 sizeof(NTW32CALL_CALLBACK_STACK));
277 AssignedStack->BaseAddress = NewStack;
278 }
279 else
280 {
281 PLIST_ENTRY StackEntry;
282
283 StackEntry = RemoveHeadList(&CallbackStackListHead);
284 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
285 AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK,
286 ListEntry);
287 NewStack = AssignedStack->BaseAddress;
288 memset(NewStack, 0, StackSize);
289 }
290 /* FIXME: Need to check whether we were interrupted from v86 mode. */
291 memcpy((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA),
292 Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
293 NewFrame = (PKTRAP_FRAME)((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA));
294 /* We need the stack pointer to remain 4-byte aligned */
295 NewFrame->Esp -= (((ArgumentLength + 3) & (~ 0x3)) + (4 * sizeof(ULONG)));
296 NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
297 UserEsp = (PULONG)NewFrame->Esp;
298 UserEsp[0] = 0; /* Return address. */
299 UserEsp[1] = RoutineIndex;
300 UserEsp[2] = (ULONG)&UserEsp[4];
301 UserEsp[3] = ArgumentLength;
302 memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength);
303
304 /* Switch to the new environment and return to user-mode. */
305 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
306 SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
307 SavedState.SavedStackBase = Thread->Tcb.StackBase;
308 SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
309 SavedState.CallerResult = Result;
310 SavedState.CallerResultLength = ResultLength;
311 SavedState.CallbackStatus = &CallbackStatus;
312 SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
313 SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
314 SavedState.SavedExceptionStack = (PVOID)KeGetCurrentKPCR()->TSS->Esp0;
315 if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
316 ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread)
317 {
318 memcpy((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA),
319 (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA),
320 sizeof(FX_SAVE_AREA));
321 }
322 Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)NewStack + StackSize;
323 Thread->Tcb.StackLimit = (ULONG)NewStack;
324 Thread->Tcb.KernelStack = (char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA);
325 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA);
326 KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
327
328 /*
329 * The callback return will have already restored most of the state we
330 * modified.
331 */
332 KeLowerIrql(DISPATCH_LEVEL);
333 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock);
334 InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
335 KeReleaseSpinLock(&CallbackStackListLock, PASSIVE_LEVEL);
336 return(CallbackStatus);
337 }
338
339 /* EOF */