2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/usercall.c
5 * PURPOSE: User-Mode callbacks. Portable part.
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
10 /* INCLUDES ******************************************************************/
14 #include <internal/debug.h>
16 /* FUNCTIONS *****************************************************************/
22 KiSwitchToUserMode(IN PVOID
*OutputBuffer
,
23 IN PULONG OutputLength
);
27 typedef struct _NTW32CALL_SAVED_STATE
29 ULONG_PTR SavedStackLimit
;
31 PVOID SavedInitialStack
;
33 PULONG CallerResultLength
;
34 PNTSTATUS CallbackStatus
;
35 PKTRAP_FRAME SavedTrapFrame
;
36 PVOID SavedCallbackStack
;
37 PVOID SavedExceptionStack
;
38 } NTW32CALL_SAVED_STATE
, *PNTW32CALL_SAVED_STATE
;
44 } NTW32CALL_CALLBACK_STACK
, *PNTW32CALL_CALLBACK_STACK
;
46 KSPIN_LOCK CallbackStackListLock
;
47 static LIST_ENTRY CallbackStackListHead
;
50 PsInitialiseW32Call(VOID
)
52 InitializeListHead(&CallbackStackListHead
);
53 KeInitializeSpinLock(&CallbackStackListLock
);
57 PsFreeCallbackStackPage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
58 PFN_TYPE Page
, SWAPENTRY SwapEntry
,
61 ASSERT(SwapEntry
== 0);
64 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
69 PsFreeCallbackStack(PVOID StackLimit
)
71 MmLockAddressSpace(MmGetKernelAddressSpace());
72 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
74 PsFreeCallbackStackPage
,
76 MmUnlockAddressSpace(MmGetKernelAddressSpace());
80 PsFreeCallbackStacks(VOID
)
82 PLIST_ENTRY CurrentListEntry
;
83 PNTW32CALL_CALLBACK_STACK Current
;
85 while (!IsListEmpty(&CallbackStackListHead
))
87 CurrentListEntry
= RemoveHeadList(&CallbackStackListHead
);
88 Current
= CONTAINING_RECORD(CurrentListEntry
, NTW32CALL_CALLBACK_STACK
,
90 PsFreeCallbackStack(Current
->BaseAddress
);
96 PsAllocateCallbackStack(ULONG StackSize
)
98 PVOID KernelStack
= NULL
;
100 PMEMORY_AREA StackArea
;
102 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
103 PPFN_TYPE Pages
= alloca(sizeof(PFN_TYPE
) * (StackSize
/PAGE_SIZE
));
106 BoundaryAddressMultiple
.QuadPart
= 0;
107 StackSize
= PAGE_ROUND_UP(StackSize
);
108 MmLockAddressSpace(MmGetKernelAddressSpace());
109 Status
= MmCreateMemoryArea(NULL
,
110 MmGetKernelAddressSpace(),
111 MEMORY_AREA_KERNEL_STACK
,
118 BoundaryAddressMultiple
);
119 MmUnlockAddressSpace(MmGetKernelAddressSpace());
120 if (!NT_SUCCESS(Status
))
122 DPRINT("Failed to create thread stack\n");
125 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
127 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Pages
[i
]);
128 if (!NT_SUCCESS(Status
))
130 for (j
= 0; j
< i
; j
++)
132 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[j
]);
137 Status
= MmCreateVirtualMapping(NULL
,
141 StackSize
/ PAGE_SIZE
);
142 if (!NT_SUCCESS(Status
))
144 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
146 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[i
]);
159 KeUserModeCallback(IN ULONG RoutineIndex
,
161 IN ULONG ArgumentLength
,
163 OUT PULONG ResultLength
)
168 PKTRAP_FRAME NewFrame
;
171 NTSTATUS CallbackStatus
;
172 NTW32CALL_SAVED_STATE SavedState
;
173 PNTW32CALL_CALLBACK_STACK AssignedStack
;
177 DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
178 RoutineIndex
, Argument
, ArgumentLength
);
180 Thread
= PsGetCurrentThread();
182 /* Set up the new kernel and user environment. */
183 StackSize
= (ULONG_PTR
)Thread
->Tcb
.StackBase
- Thread
->Tcb
.StackLimit
;
184 KeAcquireSpinLock(&CallbackStackListLock
, &oldIrql
);
185 if (IsListEmpty(&CallbackStackListHead
))
187 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
188 NewStack
= PsAllocateCallbackStack(StackSize
);
189 AssignedStack
= ExAllocatePool(NonPagedPool
,
190 sizeof(NTW32CALL_CALLBACK_STACK
));
191 AssignedStack
->BaseAddress
= NewStack
;
195 PLIST_ENTRY StackEntry
;
197 StackEntry
= RemoveHeadList(&CallbackStackListHead
);
198 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
199 AssignedStack
= CONTAINING_RECORD(StackEntry
, NTW32CALL_CALLBACK_STACK
,
201 NewStack
= AssignedStack
->BaseAddress
;
202 RtlZeroMemory(NewStack
, StackSize
);
204 /* FIXME: Need to check whether we were interrupted from v86 mode. */
205 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
),
206 Thread
->Tcb
.TrapFrame
, sizeof(KTRAP_FRAME
) - (4 * sizeof(DWORD
)));
207 NewFrame
= (PKTRAP_FRAME
)((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
));
208 /* We need the stack pointer to remain 4-byte aligned */
209 NewFrame
->Esp
-= (((ArgumentLength
+ 3) & (~ 0x3)) + (4 * sizeof(ULONG
)));
210 NewFrame
->Eip
= (ULONG
)LdrpGetSystemDllCallbackDispatcher();
211 UserEsp
= (PULONG
)NewFrame
->Esp
;
212 UserEsp
[0] = 0; /* Return address. */
213 UserEsp
[1] = RoutineIndex
;
214 UserEsp
[2] = (ULONG
)&UserEsp
[4];
215 UserEsp
[3] = ArgumentLength
;
216 RtlCopyMemory((PVOID
)&UserEsp
[4], Argument
, ArgumentLength
);
218 /* Switch to the new environment and return to user-mode. */
219 KeRaiseIrql(HIGH_LEVEL
, &oldIrql
);
220 SavedState
.SavedStackLimit
= Thread
->Tcb
.StackLimit
;
221 SavedState
.SavedStackBase
= Thread
->Tcb
.StackBase
;
222 SavedState
.SavedInitialStack
= Thread
->Tcb
.InitialStack
;
223 SavedState
.CallerResult
= Result
;
224 SavedState
.CallerResultLength
= ResultLength
;
225 SavedState
.CallbackStatus
= &CallbackStatus
;
226 SavedState
.SavedTrapFrame
= Thread
->Tcb
.TrapFrame
;
227 SavedState
.SavedCallbackStack
= Thread
->Tcb
.CallbackStack
;
228 SavedState
.SavedExceptionStack
= (PVOID
)KeGetCurrentKPCR()->TSS
->Esp0
;
229 if ((Thread
->Tcb
.NpxState
& NPX_STATE_VALID
) &&
230 ETHREAD_TO_KTHREAD(Thread
) != KeGetCurrentPrcb()->NpxThread
)
232 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(FX_SAVE_AREA
),
233 (char*)SavedState
.SavedInitialStack
- sizeof(FX_SAVE_AREA
),
234 sizeof(FX_SAVE_AREA
));
236 Thread
->Tcb
.InitialStack
= Thread
->Tcb
.StackBase
= (char*)NewStack
+ StackSize
;
237 Thread
->Tcb
.StackLimit
= (ULONG
)NewStack
;
238 Thread
->Tcb
.KernelStack
= (char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
);
239 KeGetCurrentKPCR()->TSS
->Esp0
= (ULONG
)Thread
->Tcb
.InitialStack
- sizeof(FX_SAVE_AREA
);
240 KePushAndStackSwitchAndSysRet((ULONG
)&SavedState
, Thread
->Tcb
.KernelStack
);
243 * The callback return will have already restored most of the state we
246 KeLowerIrql(DISPATCH_LEVEL
);
247 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock
);
248 InsertTailList(&CallbackStackListHead
, &AssignedStack
->ListEntry
);
249 KeReleaseSpinLock(&CallbackStackListLock
, PASSIVE_LEVEL
);
250 return(CallbackStatus
);