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