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
;
52 PsInitialiseW32Call(VOID
)
54 InitializeListHead(&CallbackStackListHead
);
55 KeInitializeSpinLock(&CallbackStackListLock
);
59 PsFreeCallbackStackPage(PVOID Context
, MEMORY_AREA
* MemoryArea
, PVOID Address
,
60 PFN_TYPE Page
, SWAPENTRY SwapEntry
,
63 ASSERT(SwapEntry
== 0);
66 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
);
71 PsFreeCallbackStack(PVOID StackLimit
)
73 MmLockAddressSpace(MmGetKernelAddressSpace());
74 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
76 PsFreeCallbackStackPage
,
78 MmUnlockAddressSpace(MmGetKernelAddressSpace());
82 PsFreeCallbackStacks(VOID
)
84 PLIST_ENTRY CurrentListEntry
;
85 PNTW32CALL_CALLBACK_STACK Current
;
87 while (!IsListEmpty(&CallbackStackListHead
))
89 CurrentListEntry
= RemoveHeadList(&CallbackStackListHead
);
90 Current
= CONTAINING_RECORD(CurrentListEntry
, NTW32CALL_CALLBACK_STACK
,
92 PsFreeCallbackStack(Current
->BaseAddress
);
98 PsAllocateCallbackStack(ULONG StackSize
)
100 PVOID KernelStack
= NULL
;
102 PMEMORY_AREA StackArea
;
104 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
105 PPFN_TYPE Pages
= alloca(sizeof(PFN_TYPE
) * (StackSize
/PAGE_SIZE
));
108 BoundaryAddressMultiple
.QuadPart
= 0;
109 StackSize
= PAGE_ROUND_UP(StackSize
);
110 MmLockAddressSpace(MmGetKernelAddressSpace());
111 Status
= MmCreateMemoryArea(NULL
,
112 MmGetKernelAddressSpace(),
113 MEMORY_AREA_KERNEL_STACK
,
120 BoundaryAddressMultiple
);
121 MmUnlockAddressSpace(MmGetKernelAddressSpace());
122 if (!NT_SUCCESS(Status
))
124 DPRINT("Failed to create thread stack\n");
127 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
129 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Pages
[i
]);
130 if (!NT_SUCCESS(Status
))
132 for (j
= 0; j
< i
; j
++)
134 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[j
]);
139 Status
= MmCreateVirtualMapping(NULL
,
143 StackSize
/ PAGE_SIZE
);
144 if (!NT_SUCCESS(Status
))
146 for (i
= 0; i
< (StackSize
/ PAGE_SIZE
); i
++)
148 MmReleasePageMemoryConsumer(MC_NPPOOL
, Pages
[i
]);
161 KeUserModeCallback(IN ULONG RoutineIndex
,
163 IN ULONG ArgumentLength
,
165 OUT PULONG ResultLength
)
170 PKTRAP_FRAME NewFrame
;
173 NTSTATUS CallbackStatus
;
174 NTW32CALL_SAVED_STATE SavedState
;
175 PNTW32CALL_CALLBACK_STACK AssignedStack
;
179 DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
180 RoutineIndex
, Argument
, ArgumentLength
);
182 Thread
= PsGetCurrentThread();
184 /* Set up the new kernel and user environment. */
185 StackSize
= (ULONG_PTR
)Thread
->Tcb
.StackBase
- Thread
->Tcb
.StackLimit
;
186 KeAcquireSpinLock(&CallbackStackListLock
, &oldIrql
);
187 if (IsListEmpty(&CallbackStackListHead
))
189 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
190 NewStack
= PsAllocateCallbackStack(StackSize
);
191 AssignedStack
= ExAllocatePool(NonPagedPool
,
192 sizeof(NTW32CALL_CALLBACK_STACK
));
193 AssignedStack
->BaseAddress
= NewStack
;
197 PLIST_ENTRY StackEntry
;
199 StackEntry
= RemoveHeadList(&CallbackStackListHead
);
200 KeReleaseSpinLock(&CallbackStackListLock
, oldIrql
);
201 AssignedStack
= CONTAINING_RECORD(StackEntry
, NTW32CALL_CALLBACK_STACK
,
203 NewStack
= AssignedStack
->BaseAddress
;
204 RtlZeroMemory(NewStack
, StackSize
);
206 /* FIXME: Need to check whether we were interrupted from v86 mode. */
207 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
),
208 Thread
->Tcb
.TrapFrame
, sizeof(KTRAP_FRAME
) - (4 * sizeof(ULONG
)));
209 NewFrame
= (PKTRAP_FRAME
)((char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
));
210 /* We need the stack pointer to remain 4-byte aligned */
211 NewFrame
->Esp
-= (((ArgumentLength
+ 3) & (~ 0x3)) + (4 * sizeof(ULONG
)));
212 NewFrame
->Eip
= (ULONG
)KeUserCallbackDispatcher
;
213 UserEsp
= (PULONG
)NewFrame
->Esp
;
214 UserEsp
[0] = 0; /* Return address. */
215 UserEsp
[1] = RoutineIndex
;
216 UserEsp
[2] = (ULONG
)&UserEsp
[4];
217 UserEsp
[3] = ArgumentLength
;
218 RtlCopyMemory((PVOID
)&UserEsp
[4], Argument
, ArgumentLength
);
220 /* Switch to the new environment and return to user-mode. */
221 KeRaiseIrql(HIGH_LEVEL
, &oldIrql
);
222 SavedState
.SavedStackLimit
= Thread
->Tcb
.StackLimit
;
223 SavedState
.SavedStackBase
= Thread
->Tcb
.StackBase
;
224 SavedState
.SavedInitialStack
= Thread
->Tcb
.InitialStack
;
225 SavedState
.CallerResult
= Result
;
226 SavedState
.CallerResultLength
= ResultLength
;
227 SavedState
.CallbackStatus
= &CallbackStatus
;
228 SavedState
.SavedTrapFrame
= Thread
->Tcb
.TrapFrame
;
229 SavedState
.SavedCallbackStack
= Thread
->Tcb
.CallbackStack
;
230 SavedState
.SavedExceptionStack
= (PVOID
)KeGetCurrentKPCR()->TSS
->Esp0
;
231 if ((Thread
->Tcb
.NpxState
& NPX_STATE_VALID
) &&
232 &Thread
->Tcb
!= KeGetCurrentPrcb()->NpxThread
)
234 RtlCopyMemory((char*)NewStack
+ StackSize
- sizeof(FX_SAVE_AREA
),
235 (char*)SavedState
.SavedInitialStack
- sizeof(FX_SAVE_AREA
),
236 sizeof(FX_SAVE_AREA
));
238 Thread
->Tcb
.InitialStack
= Thread
->Tcb
.StackBase
= (char*)NewStack
+ StackSize
;
239 Thread
->Tcb
.StackLimit
= (ULONG
)NewStack
;
240 Thread
->Tcb
.KernelStack
= (char*)NewStack
+ StackSize
- sizeof(KTRAP_FRAME
) - sizeof(FX_SAVE_AREA
);
241 KeGetCurrentKPCR()->TSS
->Esp0
= (ULONG
)Thread
->Tcb
.InitialStack
- sizeof(FX_SAVE_AREA
) - 0x10;
242 KePushAndStackSwitchAndSysRet((ULONG
)&SavedState
, Thread
->Tcb
.KernelStack
);
245 * The callback return will have already restored most of the state we
248 KeLowerIrql(DISPATCH_LEVEL
);
249 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock
);
250 InsertTailList(&CallbackStackListHead
, &AssignedStack
->ListEntry
);
251 KeReleaseSpinLock(&CallbackStackListLock
, PASSIVE_LEVEL
);
252 return(CallbackStatus
);