3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/w32call.c
6 * PURPOSE: Thread managment
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
15 * All of the routines that manipulate the thread queue synchronize on
20 /* INCLUDES ****************************************************************/
24 #include <internal/debug.h>
27 /* void * alloca(size_t size); */
28 #elif defined(_MSC_VER)
29 void* _alloca(size_t size
);
31 #error Unknown compiler for alloca intrinsic stack allocation "function"
34 /* TYPES *******************************************************************/
36 typedef struct _NTW32CALL_SAVED_STATE
38 ULONG_PTR SavedStackLimit
;
40 PVOID SavedInitialStack
;
42 PULONG CallerResultLength
;
43 PNTSTATUS CallbackStatus
;
44 PKTRAP_FRAME SavedTrapFrame
;
45 PVOID SavedCallbackStack
;
46 PVOID SavedExceptionStack
;
47 } NTW32CALL_SAVED_STATE
, *PNTW32CALL_SAVED_STATE
;
53 } NTW32CALL_CALLBACK_STACK
, *PNTW32CALL_CALLBACK_STACK
;
55 KSPIN_LOCK CallbackStackListLock
;
56 static LIST_ENTRY CallbackStackListHead
;
58 /* FUNCTIONS ***************************************************************/
61 PsInitialiseW32Call(VOID
)
63 InitializeListHead(&CallbackStackListHead
);
64 KeInitializeSpinLock(&CallbackStackListLock
);
68 NtCallbackReturn (PVOID Result
,
74 PNTSTATUS CallbackStatus
;
75 PULONG CallerResultLength
;
81 PNTW32CALL_SAVED_STATE State
;
82 PKTRAP_FRAME SavedTrapFrame
;
83 PVOID SavedCallbackStack
;
84 PVOID SavedExceptionStack
;
86 Thread
= PsGetCurrentThread();
87 if (Thread
->Tcb
.CallbackStack
== NULL
)
89 return(STATUS_NO_CALLBACK_ACTIVE
);
92 OldStack
= (PULONG
)Thread
->Tcb
.CallbackStack
;
95 * Get the values that NtW32Call left on the inactive stack for us.
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
;
109 * Copy the callback status and the callback result to NtW32Call
111 *CallbackStatus
= Status
;
112 if (CallerResult
!= NULL
&& CallerResultLength
!= NULL
)
116 *CallerResultLength
= 0;
120 *CallerResultLength
= min(ResultLength
, *CallerResultLength
);
121 memcpy(*CallerResult
, Result
, *CallerResultLength
);
126 * Restore the old stack.
128 KeRaiseIrql(HIGH_LEVEL
, &oldIrql
);
129 if ((Thread
->Tcb
.NpxState
& NPX_STATE_VALID
) &&
130 ETHREAD_TO_KTHREAD(Thread
) != KeGetCurrentKPCR()->PrcbData
.NpxThread
)
132 memcpy((char*)InitialStack
- sizeof(FX_SAVE_AREA
),
133 (char*)Thread
->Tcb
.InitialStack
- sizeof(FX_SAVE_AREA
),
134 sizeof(FX_SAVE_AREA
));
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));
144 /* Should never return. */
146 return(STATUS_UNSUCCESSFUL
);
150 PsFreeCallbackStackPage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
151 PFN_TYPE Page
, SWAPENTRY SwapEntry
,
154 ASSERT(SwapEntry
== 0);
157 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
162 PsFreeCallbackStack(PVOID StackLimit
)
164 MmLockAddressSpace(MmGetKernelAddressSpace());
165 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
167 PsFreeCallbackStackPage
,
169 MmUnlockAddressSpace(MmGetKernelAddressSpace());
173 PsFreeCallbackStacks(VOID
)
175 PLIST_ENTRY CurrentListEntry
;
176 PNTW32CALL_CALLBACK_STACK Current
;
178 while (!IsListEmpty(&CallbackStackListHead
))
180 CurrentListEntry
= RemoveHeadList(&CallbackStackListHead
);
181 Current
= CONTAINING_RECORD(CurrentListEntry
, NTW32CALL_CALLBACK_STACK
,
183 PsFreeCallbackStack(Current
->BaseAddress
);
189 PsAllocateCallbackStack(ULONG StackSize
)
191 PVOID KernelStack
= NULL
;
193 PMEMORY_AREA StackArea
;
195 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
196 PPFN_TYPE Pages
= alloca(sizeof(PFN_TYPE
) * (StackSize
/PAGE_SIZE
));
199 BoundaryAddressMultiple
.QuadPart
= 0;
200 StackSize
= PAGE_ROUND_UP(StackSize
);
201 MmLockAddressSpace(MmGetKernelAddressSpace());
202 Status
= MmCreateMemoryArea(NULL
,
203 MmGetKernelAddressSpace(),
204 MEMORY_AREA_KERNEL_STACK
,
211 BoundaryAddressMultiple
);
212 MmUnlockAddressSpace(MmGetKernelAddressSpace());
213 if (!NT_SUCCESS(Status
))
215 DPRINT("Failed to create thread stack\n");
218 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
220 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Pages
[i
]);
221 if (!NT_SUCCESS(Status
))
223 for (j
= 0; j
< i
; j
++)
225 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[j
]);
230 Status
= MmCreateVirtualMapping(NULL
,
234 StackSize
/ PAGE_SIZE
);
235 if (!NT_SUCCESS(Status
))
237 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
239 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[i
]);
247 NtW32Call (IN ULONG RoutineIndex
,
249 IN ULONG ArgumentLength
,
250 OUT PVOID
* Result OPTIONAL
,
251 OUT PULONG ResultLength OPTIONAL
)
256 PKTRAP_FRAME NewFrame
;
259 NTSTATUS CallbackStatus
;
260 NTW32CALL_SAVED_STATE SavedState
;
261 PNTW32CALL_CALLBACK_STACK AssignedStack
;
263 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
264 RoutineIndex
, Argument
, ArgumentLength
);
266 Thread
= PsGetCurrentThread();
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
))
273 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
274 NewStack
= PsAllocateCallbackStack(StackSize
);
275 AssignedStack
= ExAllocatePool(NonPagedPool
,
276 sizeof(NTW32CALL_CALLBACK_STACK
));
277 AssignedStack
->BaseAddress
= NewStack
;
281 PLIST_ENTRY StackEntry
;
283 StackEntry
= RemoveHeadList(&CallbackStackListHead
);
284 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
285 AssignedStack
= CONTAINING_RECORD(StackEntry
, NTW32CALL_CALLBACK_STACK
,
287 NewStack
= AssignedStack
->BaseAddress
;
288 memset(NewStack
, 0, StackSize
);
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
);
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
)
318 memcpy((char*)NewStack
+ StackSize
- sizeof(FX_SAVE_AREA
),
319 (char*)SavedState
.SavedInitialStack
- sizeof(FX_SAVE_AREA
),
320 sizeof(FX_SAVE_AREA
));
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
);
329 * The callback return will have already restored most of the state we
332 KeLowerIrql(DISPATCH_LEVEL
);
333 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock
);
334 InsertTailList(&CallbackStackListHead
, &AssignedStack
->ListEntry
);
335 KeReleaseSpinLock(&CallbackStackListLock
, PASSIVE_LEVEL
);
336 return(CallbackStatus
);