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 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, PsInitialiseW32Call)
20 /* FUNCTIONS *****************************************************************/
26 KiSwitchToUserMode(IN PVOID
*OutputBuffer
,
27 IN PULONG OutputLength
);
31 typedef struct _NTW32CALL_SAVED_STATE
33 ULONG_PTR SavedStackLimit
;
35 PVOID SavedInitialStack
;
37 PULONG CallerResultLength
;
38 PNTSTATUS CallbackStatus
;
39 PKTRAP_FRAME SavedTrapFrame
;
40 PVOID SavedCallbackStack
;
41 PVOID SavedExceptionStack
;
42 } NTW32CALL_SAVED_STATE
, *PNTW32CALL_SAVED_STATE
;
48 } NTW32CALL_CALLBACK_STACK
, *PNTW32CALL_CALLBACK_STACK
;
50 KSPIN_LOCK CallbackStackListLock
;
51 static LIST_ENTRY CallbackStackListHead
;
56 PsInitialiseW32Call(VOID
)
58 InitializeListHead(&CallbackStackListHead
);
59 KeInitializeSpinLock(&CallbackStackListLock
);
63 PsFreeCallbackStackPage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
64 PFN_TYPE Page
, SWAPENTRY SwapEntry
,
67 ASSERT(SwapEntry
== 0);
70 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
75 PsFreeCallbackStack(PVOID StackLimit
)
77 MmLockAddressSpace(MmGetKernelAddressSpace());
78 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
80 PsFreeCallbackStackPage
,
82 MmUnlockAddressSpace(MmGetKernelAddressSpace());
86 PsFreeCallbackStacks(VOID
)
88 PLIST_ENTRY CurrentListEntry
;
89 PNTW32CALL_CALLBACK_STACK Current
;
91 while (!IsListEmpty(&CallbackStackListHead
))
93 CurrentListEntry
= RemoveHeadList(&CallbackStackListHead
);
94 Current
= CONTAINING_RECORD(CurrentListEntry
, NTW32CALL_CALLBACK_STACK
,
96 PsFreeCallbackStack(Current
->BaseAddress
);
102 PsAllocateCallbackStack(ULONG StackSize
)
104 PVOID KernelStack
= NULL
;
106 PMEMORY_AREA StackArea
;
108 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
109 PPFN_TYPE Pages
= alloca(sizeof(PFN_TYPE
) * (StackSize
/PAGE_SIZE
));
112 BoundaryAddressMultiple
.QuadPart
= 0;
113 StackSize
= PAGE_ROUND_UP(StackSize
);
114 MmLockAddressSpace(MmGetKernelAddressSpace());
115 Status
= MmCreateMemoryArea(MmGetKernelAddressSpace(),
116 MEMORY_AREA_KERNEL_STACK
,
123 BoundaryAddressMultiple
);
124 MmUnlockAddressSpace(MmGetKernelAddressSpace());
125 if (!NT_SUCCESS(Status
))
127 DPRINT("Failed to create thread stack\n");
130 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
132 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Pages
[i
]);
133 if (!NT_SUCCESS(Status
))
135 for (j
= 0; j
< i
; j
++)
137 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[j
]);
142 Status
= MmCreateVirtualMapping(NULL
,
146 StackSize
/ PAGE_SIZE
);
147 if (!NT_SUCCESS(Status
))
149 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
151 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[i
]);
164 KeUserModeCallback(IN ULONG RoutineIndex
,
166 IN ULONG ArgumentLength
,
168 OUT PULONG ResultLength
)
173 PKTRAP_FRAME NewFrame
;
176 NTSTATUS CallbackStatus
;
177 NTW32CALL_SAVED_STATE SavedState
;
178 PNTW32CALL_CALLBACK_STACK AssignedStack
;
182 DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
183 RoutineIndex
, Argument
, ArgumentLength
);
185 Thread
= PsGetCurrentThread();
187 /* Set up the new kernel and user environment. */
188 StackSize
= (ULONG_PTR
)Thread
->Tcb
.StackBase
- Thread
->Tcb
.StackLimit
;
189 KeAcquireSpinLock(&CallbackStackListLock
, &oldIrql
);
190 if (IsListEmpty(&CallbackStackListHead
))
192 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
193 NewStack
= PsAllocateCallbackStack(StackSize
);
194 AssignedStack
= ExAllocatePool(NonPagedPool
,
195 sizeof(NTW32CALL_CALLBACK_STACK
));
196 AssignedStack
->BaseAddress
= NewStack
;
200 PLIST_ENTRY StackEntry
;
202 StackEntry
= RemoveHeadList(&CallbackStackListHead
);
203 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
204 AssignedStack
= CONTAINING_RECORD(StackEntry
, NTW32CALL_CALLBACK_STACK
,
206 NewStack
= AssignedStack
->BaseAddress
;
207 RtlZeroMemory(NewStack
, StackSize
);
209 /* FIXME: Need to check whether we were interrupted from v86 mode. */
210 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
),
211 Thread
->Tcb
.TrapFrame
, sizeof(KTRAP_FRAME
) - (4 * sizeof(ULONG
)));
212 NewFrame
= (PKTRAP_FRAME
)((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
));
213 /* We need the stack pointer to remain 4-byte aligned */
214 NewFrame
->HardwareEsp
-= (((ArgumentLength
+ 3) & (~ 0x3)) + (4 * sizeof(ULONG
)));
215 NewFrame
->Eip
= (ULONG
)KeUserCallbackDispatcher
;
216 UserEsp
= (PULONG
)NewFrame
->HardwareEsp
;
217 UserEsp
[0] = 0; /* Return address. */
218 UserEsp
[1] = RoutineIndex
;
219 UserEsp
[2] = (ULONG
)&UserEsp
[4];
220 UserEsp
[3] = ArgumentLength
;
221 RtlCopyMemory((PVOID
)&UserEsp
[4], Argument
, ArgumentLength
);
223 /* Switch to the new environment and return to user-mode. */
224 KeRaiseIrql(HIGH_LEVEL
, &oldIrql
);
225 SavedState
.SavedStackLimit
= Thread
->Tcb
.StackLimit
;
226 SavedState
.SavedStackBase
= Thread
->Tcb
.StackBase
;
227 SavedState
.SavedInitialStack
= Thread
->Tcb
.InitialStack
;
228 SavedState
.CallerResult
= Result
;
229 SavedState
.CallerResultLength
= ResultLength
;
230 SavedState
.CallbackStatus
= &CallbackStatus
;
231 SavedState
.SavedTrapFrame
= Thread
->Tcb
.TrapFrame
;
232 SavedState
.SavedCallbackStack
= Thread
->Tcb
.CallbackStack
;
233 SavedState
.SavedExceptionStack
= (PVOID
)KeGetCurrentKPCR()->TSS
->Esp0
;
234 if ((Thread
->Tcb
.NpxState
& NPX_STATE_VALID
) &&
235 &Thread
->Tcb
!= KeGetCurrentPrcb()->NpxThread
)
237 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(FX_SAVE_AREA
),
238 (char*)SavedState
.SavedInitialStack
- sizeof(FX_SAVE_AREA
),
239 sizeof(FX_SAVE_AREA
));
241 Thread
->Tcb
.InitialStack
= Thread
->Tcb
.StackBase
= (char*)NewStack
+ StackSize
;
242 Thread
->Tcb
.StackLimit
= (ULONG
)NewStack
;
243 Thread
->Tcb
.KernelStack
= (char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
);
244 KeGetCurrentKPCR()->TSS
->Esp0
= (ULONG
)Thread
->Tcb
.InitialStack
- sizeof(FX_SAVE_AREA
) - 0x10;
245 KePushAndStackSwitchAndSysRet((ULONG
)&SavedState
, Thread
->Tcb
.KernelStack
);
248 * The callback return will have already restored most of the state we
251 KeLowerIrql(DISPATCH_LEVEL
);
252 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock
);
253 InsertTailList(&CallbackStackListHead
, &AssignedStack
->ListEntry
);
254 KeReleaseSpinLock(&CallbackStackListLock
, PASSIVE_LEVEL
);
255 return(CallbackStatus
);