1 /* $Id: w32call.c,v 1.14 2004/08/15 16:39:10 chorns Exp $
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 ****************************************************************/
25 #include <internal/debug.h>
28 void * alloca(size_t size
);
29 #elif defined(_MSC_VER)
30 void* _alloca(size_t size
);
32 #error Unknown compiler for alloca intrinsic stack allocation "function"
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
;
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
;
85 Thread
= PsGetCurrentThread();
86 if (Thread
->Tcb
.CallbackStack
== NULL
)
88 return(STATUS_NO_CALLBACK_ACTIVE
);
91 OldStack
= (PULONG
)Thread
->Tcb
.CallbackStack
;
94 * Get the values that NtW32Call left on the inactive stack for us.
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
;
107 * Copy the callback status and the callback result to NtW32Call
109 *CallbackStatus
= Status
;
110 if (CallerResult
!= NULL
&& CallerResultLength
!= NULL
)
114 *CallerResultLength
= 0;
118 *CallerResultLength
= min(ResultLength
, *CallerResultLength
);
119 memcpy(*CallerResult
, Result
, *CallerResultLength
);
124 * Restore the old stack.
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));
135 /* Should never return. */
137 return(STATUS_UNSUCCESSFUL
);
141 PsFreeCallbackStackPage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
142 PFN_TYPE Page
, SWAPENTRY SwapEntry
,
145 assert(SwapEntry
== 0);
148 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
153 PsFreeCallbackStack(PVOID StackLimit
)
155 MmLockAddressSpace(MmGetKernelAddressSpace());
156 MmFreeMemoryArea(MmGetKernelAddressSpace(),
159 PsFreeCallbackStackPage
,
161 MmUnlockAddressSpace(MmGetKernelAddressSpace());
165 PsFreeCallbackStacks(VOID
)
167 PLIST_ENTRY CurrentListEntry
;
168 PNTW32CALL_CALLBACK_STACK Current
;
170 while (!IsListEmpty(&CallbackStackListHead
))
172 CurrentListEntry
= RemoveHeadList(&CallbackStackListHead
);
173 Current
= CONTAINING_RECORD(CurrentListEntry
, NTW32CALL_CALLBACK_STACK
,
175 PsFreeCallbackStack(Current
->BaseAddress
);
181 PsAllocateCallbackStack(ULONG StackSize
)
183 PVOID KernelStack
= NULL
;
185 PMEMORY_AREA StackArea
;
187 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
188 PPFN_TYPE Pages
= alloca(sizeof(PFN_TYPE
) * (StackSize
/PAGE_SIZE
));
191 BoundaryAddressMultiple
.QuadPart
= 0;
192 StackSize
= PAGE_ROUND_UP(StackSize
);
193 MmLockAddressSpace(MmGetKernelAddressSpace());
194 Status
= MmCreateMemoryArea(NULL
,
195 MmGetKernelAddressSpace(),
196 MEMORY_AREA_KERNEL_STACK
,
203 BoundaryAddressMultiple
);
204 MmUnlockAddressSpace(MmGetKernelAddressSpace());
205 if (!NT_SUCCESS(Status
))
207 DPRINT("Failed to create thread stack\n");
210 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
212 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Pages
[i
]);
213 if (!NT_SUCCESS(Status
))
215 for (j
= 0; j
< i
; j
++)
217 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[j
]);
222 Status
= MmCreateVirtualMapping(NULL
,
226 StackSize
/ PAGE_SIZE
);
227 if (!NT_SUCCESS(Status
))
229 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
231 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[i
]);
239 NtW32Call (IN ULONG RoutineIndex
,
241 IN ULONG ArgumentLength
,
242 OUT PVOID
* Result OPTIONAL
,
243 OUT PULONG ResultLength OPTIONAL
)
248 PKTRAP_FRAME NewFrame
;
251 NTSTATUS CallbackStatus
;
252 NTW32CALL_SAVED_STATE SavedState
;
253 PNTW32CALL_CALLBACK_STACK AssignedStack
;
255 DPRINT("NtW32Call(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
256 RoutineIndex
, Argument
, ArgumentLength
);
258 Thread
= PsGetCurrentThread();
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
))
265 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
266 NewStack
= PsAllocateCallbackStack(StackSize
);
267 AssignedStack
= ExAllocatePool(NonPagedPool
,
268 sizeof(NTW32CALL_CALLBACK_STACK
));
269 AssignedStack
->BaseAddress
= NewStack
;
273 PLIST_ENTRY StackEntry
;
275 StackEntry
= RemoveHeadList(&CallbackStackListHead
);
276 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
277 AssignedStack
= CONTAINING_RECORD(StackEntry
, NTW32CALL_CALLBACK_STACK
,
279 NewStack
= AssignedStack
->BaseAddress
;
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
);
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
);
311 * The callback return will have already restored most of the state we
314 KeLowerIrql(DISPATCH_LEVEL
);
315 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock
);
316 InsertTailList(&CallbackStackListHead
, &AssignedStack
->ListEntry
);
317 KeReleaseSpinLock(&CallbackStackListLock
, PASSIVE_LEVEL
);
318 return(CallbackStatus
);