Little KDB update ;-) If you have any problems and/or questions let me know. I hope...
[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 PAGED_CODE();
87
88 Thread = PsGetCurrentThread();
89 if (Thread->Tcb.CallbackStack == NULL)
90 {
91 return(STATUS_NO_CALLBACK_ACTIVE);
92 }
93
94 OldStack = (PULONG)Thread->Tcb.CallbackStack;
95
96 /*
97 * Get the values that NtW32Call left on the inactive stack for us.
98 */
99 State = (PNTW32CALL_SAVED_STATE)OldStack[0];
100 CallbackStatus = State->CallbackStatus;
101 CallerResultLength = State->CallerResultLength;
102 CallerResult = State->CallerResult;
103 InitialStack = State->SavedInitialStack;
104 StackBase = State->SavedStackBase;
105 StackLimit = State->SavedStackLimit;
106 SavedTrapFrame = State->SavedTrapFrame;
107 SavedCallbackStack = State->SavedCallbackStack;
108 SavedExceptionStack = State->SavedExceptionStack;
109
110 /*
111 * Copy the callback status and the callback result to NtW32Call
112 */
113 *CallbackStatus = Status;
114 if (CallerResult != NULL && CallerResultLength != NULL)
115 {
116 if (Result == NULL)
117 {
118 *CallerResultLength = 0;
119 }
120 else
121 {
122 *CallerResultLength = min(ResultLength, *CallerResultLength);
123 RtlCopyMemory(*CallerResult, Result, *CallerResultLength);
124 }
125 }
126
127 /*
128 * Restore the old stack.
129 */
130 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
131 if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
132 ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread)
133 {
134 RtlCopyMemory((char*)InitialStack - sizeof(FX_SAVE_AREA),
135 (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA),
136 sizeof(FX_SAVE_AREA));
137 }
138 Thread->Tcb.InitialStack = InitialStack;
139 Thread->Tcb.StackBase = StackBase;
140 Thread->Tcb.StackLimit = StackLimit;
141 Thread->Tcb.TrapFrame = SavedTrapFrame;
142 Thread->Tcb.CallbackStack = SavedCallbackStack;
143 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)SavedExceptionStack;
144 KeStackSwitchAndRet((PVOID)(OldStack + 1));
145
146 /* Should never return. */
147 KEBUGCHECK(0);
148 return(STATUS_UNSUCCESSFUL);
149 }
150
151 VOID STATIC
152 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
153 PFN_TYPE Page, SWAPENTRY SwapEntry,
154 BOOLEAN Dirty)
155 {
156 ASSERT(SwapEntry == 0);
157 if (Page != 0)
158 {
159 MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
160 }
161 }
162
163 VOID STATIC
164 PsFreeCallbackStack(PVOID StackLimit)
165 {
166 MmLockAddressSpace(MmGetKernelAddressSpace());
167 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
168 StackLimit,
169 PsFreeCallbackStackPage,
170 NULL);
171 MmUnlockAddressSpace(MmGetKernelAddressSpace());
172 }
173
174 VOID
175 PsFreeCallbackStacks(VOID)
176 {
177 PLIST_ENTRY CurrentListEntry;
178 PNTW32CALL_CALLBACK_STACK Current;
179
180 while (!IsListEmpty(&CallbackStackListHead))
181 {
182 CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
183 Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
184 ListEntry);
185 PsFreeCallbackStack(Current->BaseAddress);
186 ExFreePool(Current);
187 }
188 }
189
190 PVOID STATIC
191 PsAllocateCallbackStack(ULONG StackSize)
192 {
193 PVOID KernelStack = NULL;
194 NTSTATUS Status;
195 PMEMORY_AREA StackArea;
196 ULONG i, j;
197 PHYSICAL_ADDRESS BoundaryAddressMultiple;
198 PPFN_TYPE Pages = alloca(sizeof(PFN_TYPE) * (StackSize /PAGE_SIZE));
199
200
201 BoundaryAddressMultiple.QuadPart = 0;
202 StackSize = PAGE_ROUND_UP(StackSize);
203 MmLockAddressSpace(MmGetKernelAddressSpace());
204 Status = MmCreateMemoryArea(NULL,
205 MmGetKernelAddressSpace(),
206 MEMORY_AREA_KERNEL_STACK,
207 &KernelStack,
208 StackSize,
209 0,
210 &StackArea,
211 FALSE,
212 FALSE,
213 BoundaryAddressMultiple);
214 MmUnlockAddressSpace(MmGetKernelAddressSpace());
215 if (!NT_SUCCESS(Status))
216 {
217 DPRINT("Failed to create thread stack\n");
218 return(NULL);
219 }
220 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
221 {
222 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Pages[i]);
223 if (!NT_SUCCESS(Status))
224 {
225 for (j = 0; j < i; j++)
226 {
227 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[j]);
228 }
229 return(NULL);
230 }
231 }
232 Status = MmCreateVirtualMapping(NULL,
233 KernelStack,
234 PAGE_READWRITE,
235 Pages,
236 StackSize / PAGE_SIZE);
237 if (!NT_SUCCESS(Status))
238 {
239 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
240 {
241 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[i]);
242 }
243 return(NULL);
244 }
245 return(KernelStack);
246 }
247
248 NTSTATUS STDCALL
249 NtW32Call (IN ULONG RoutineIndex,
250 IN PVOID Argument,
251 IN ULONG ArgumentLength,
252 OUT PVOID* Result OPTIONAL,
253 OUT PULONG ResultLength OPTIONAL)
254 {
255 PETHREAD Thread;
256 PVOID NewStack;
257 ULONG_PTR StackSize;
258 PKTRAP_FRAME NewFrame;
259 PULONG UserEsp;
260 KIRQL oldIrql;
261 NTSTATUS CallbackStatus;
262 NTW32CALL_SAVED_STATE SavedState;
263 PNTW32CALL_CALLBACK_STACK AssignedStack;
264
265 PAGED_CODE();
266
267 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
268 RoutineIndex, Argument, ArgumentLength);
269
270 Thread = PsGetCurrentThread();
271
272 /* Set up the new kernel and user environment. */
273 StackSize = (ULONG_PTR)Thread->Tcb.StackBase - Thread->Tcb.StackLimit;
274 KeAcquireSpinLock(&CallbackStackListLock, &oldIrql);
275 if (IsListEmpty(&CallbackStackListHead))
276 {
277 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
278 NewStack = PsAllocateCallbackStack(StackSize);
279 AssignedStack = ExAllocatePool(NonPagedPool,
280 sizeof(NTW32CALL_CALLBACK_STACK));
281 AssignedStack->BaseAddress = NewStack;
282 }
283 else
284 {
285 PLIST_ENTRY StackEntry;
286
287 StackEntry = RemoveHeadList(&CallbackStackListHead);
288 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
289 AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK,
290 ListEntry);
291 NewStack = AssignedStack->BaseAddress;
292 RtlZeroMemory(NewStack, StackSize);
293 }
294 /* FIXME: Need to check whether we were interrupted from v86 mode. */
295 RtlCopyMemory((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA),
296 Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
297 NewFrame = (PKTRAP_FRAME)((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA));
298 /* We need the stack pointer to remain 4-byte aligned */
299 NewFrame->Esp -= (((ArgumentLength + 3) & (~ 0x3)) + (4 * sizeof(ULONG)));
300 NewFrame->Eip = (ULONG)LdrpGetSystemDllCallbackDispatcher();
301 UserEsp = (PULONG)NewFrame->Esp;
302 UserEsp[0] = 0; /* Return address. */
303 UserEsp[1] = RoutineIndex;
304 UserEsp[2] = (ULONG)&UserEsp[4];
305 UserEsp[3] = ArgumentLength;
306 RtlCopyMemory((PVOID)&UserEsp[4], Argument, ArgumentLength);
307
308 /* Switch to the new environment and return to user-mode. */
309 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
310 SavedState.SavedStackLimit = Thread->Tcb.StackLimit;
311 SavedState.SavedStackBase = Thread->Tcb.StackBase;
312 SavedState.SavedInitialStack = Thread->Tcb.InitialStack;
313 SavedState.CallerResult = Result;
314 SavedState.CallerResultLength = ResultLength;
315 SavedState.CallbackStatus = &CallbackStatus;
316 SavedState.SavedTrapFrame = Thread->Tcb.TrapFrame;
317 SavedState.SavedCallbackStack = Thread->Tcb.CallbackStack;
318 SavedState.SavedExceptionStack = (PVOID)KeGetCurrentKPCR()->TSS->Esp0;
319 if ((Thread->Tcb.NpxState & NPX_STATE_VALID) &&
320 ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread)
321 {
322 RtlCopyMemory((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA),
323 (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA),
324 sizeof(FX_SAVE_AREA));
325 }
326 Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)NewStack + StackSize;
327 Thread->Tcb.StackLimit = (ULONG)NewStack;
328 Thread->Tcb.KernelStack = (char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA);
329 KeGetCurrentKPCR()->TSS->Esp0 = (ULONG)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA);
330 KePushAndStackSwitchAndSysRet((ULONG)&SavedState, Thread->Tcb.KernelStack);
331
332 /*
333 * The callback return will have already restored most of the state we
334 * modified.
335 */
336 KeLowerIrql(DISPATCH_LEVEL);
337 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock);
338 InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
339 KeReleaseSpinLock(&CallbackStackListLock, PASSIVE_LEVEL);
340 return(CallbackStatus);
341 }
342
343 /* EOF */