Merge 14981:15268 from trunk
[reactos.git] / reactos / ntoskrnl / ke / usercall.c
1 /*
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.
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <internal/debug.h>
15
16 /* FUNCTIONS *****************************************************************/
17
18 #if ALEX_CB_REWRITE
19
20 NTSTATUS
21 STDCALL
22 KiSwitchToUserMode(IN PVOID *OutputBuffer,
23 IN PULONG OutputLength);
24
25 #else
26
27 typedef struct _NTW32CALL_SAVED_STATE
28 {
29 ULONG_PTR SavedStackLimit;
30 PVOID SavedStackBase;
31 PVOID SavedInitialStack;
32 PVOID CallerResult;
33 PULONG CallerResultLength;
34 PNTSTATUS CallbackStatus;
35 PKTRAP_FRAME SavedTrapFrame;
36 PVOID SavedCallbackStack;
37 PVOID SavedExceptionStack;
38 } NTW32CALL_SAVED_STATE, *PNTW32CALL_SAVED_STATE;
39
40 typedef struct
41 {
42 PVOID BaseAddress;
43 LIST_ENTRY ListEntry;
44 } NTW32CALL_CALLBACK_STACK, *PNTW32CALL_CALLBACK_STACK;
45
46 KSPIN_LOCK CallbackStackListLock;
47 static LIST_ENTRY CallbackStackListHead;
48
49 VOID INIT_FUNCTION
50 PsInitialiseW32Call(VOID)
51 {
52 InitializeListHead(&CallbackStackListHead);
53 KeInitializeSpinLock(&CallbackStackListLock);
54 }
55
56 VOID STATIC
57 PsFreeCallbackStackPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
58 PFN_TYPE Page, SWAPENTRY SwapEntry,
59 BOOLEAN Dirty)
60 {
61 ASSERT(SwapEntry == 0);
62 if (Page != 0)
63 {
64 MmReleasePageMemoryConsumer(MC_NPPOOL, Page);
65 }
66 }
67
68 VOID STATIC
69 PsFreeCallbackStack(PVOID StackLimit)
70 {
71 MmLockAddressSpace(MmGetKernelAddressSpace());
72 MmFreeMemoryAreaByPtr(MmGetKernelAddressSpace(),
73 StackLimit,
74 PsFreeCallbackStackPage,
75 NULL);
76 MmUnlockAddressSpace(MmGetKernelAddressSpace());
77 }
78
79 VOID
80 PsFreeCallbackStacks(VOID)
81 {
82 PLIST_ENTRY CurrentListEntry;
83 PNTW32CALL_CALLBACK_STACK Current;
84
85 while (!IsListEmpty(&CallbackStackListHead))
86 {
87 CurrentListEntry = RemoveHeadList(&CallbackStackListHead);
88 Current = CONTAINING_RECORD(CurrentListEntry, NTW32CALL_CALLBACK_STACK,
89 ListEntry);
90 PsFreeCallbackStack(Current->BaseAddress);
91 ExFreePool(Current);
92 }
93 }
94
95 PVOID STATIC
96 PsAllocateCallbackStack(ULONG StackSize)
97 {
98 PVOID KernelStack = NULL;
99 NTSTATUS Status;
100 PMEMORY_AREA StackArea;
101 ULONG i, j;
102 PHYSICAL_ADDRESS BoundaryAddressMultiple;
103 PPFN_TYPE Pages = alloca(sizeof(PFN_TYPE) * (StackSize /PAGE_SIZE));
104
105
106 BoundaryAddressMultiple.QuadPart = 0;
107 StackSize = PAGE_ROUND_UP(StackSize);
108 MmLockAddressSpace(MmGetKernelAddressSpace());
109 Status = MmCreateMemoryArea(NULL,
110 MmGetKernelAddressSpace(),
111 MEMORY_AREA_KERNEL_STACK,
112 &KernelStack,
113 StackSize,
114 0,
115 &StackArea,
116 FALSE,
117 FALSE,
118 BoundaryAddressMultiple);
119 MmUnlockAddressSpace(MmGetKernelAddressSpace());
120 if (!NT_SUCCESS(Status))
121 {
122 DPRINT("Failed to create thread stack\n");
123 return(NULL);
124 }
125 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
126 {
127 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, TRUE, &Pages[i]);
128 if (!NT_SUCCESS(Status))
129 {
130 for (j = 0; j < i; j++)
131 {
132 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[j]);
133 }
134 return(NULL);
135 }
136 }
137 Status = MmCreateVirtualMapping(NULL,
138 KernelStack,
139 PAGE_READWRITE,
140 Pages,
141 StackSize / PAGE_SIZE);
142 if (!NT_SUCCESS(Status))
143 {
144 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
145 {
146 MmReleasePageMemoryConsumer(MC_NPPOOL, Pages[i]);
147 }
148 return(NULL);
149 }
150 return(KernelStack);
151 }
152 #endif
153
154 /*
155 * @implemented
156 */
157 NTSTATUS
158 STDCALL
159 KeUserModeCallback(IN ULONG RoutineIndex,
160 IN PVOID Argument,
161 IN ULONG ArgumentLength,
162 OUT PVOID *Result,
163 OUT PULONG ResultLength)
164 {
165 PETHREAD Thread;
166 PVOID NewStack;
167 ULONG_PTR StackSize;
168 PKTRAP_FRAME NewFrame;
169 PULONG UserEsp;
170 KIRQL oldIrql;
171 NTSTATUS CallbackStatus;
172 NTW32CALL_SAVED_STATE SavedState;
173 PNTW32CALL_CALLBACK_STACK AssignedStack;
174
175 PAGED_CODE();
176
177 DPRINT("KeUserModeCallback(RoutineIndex %d, Argument %X, ArgumentLength %d)\n",
178 RoutineIndex, Argument, ArgumentLength);
179
180 Thread = PsGetCurrentThread();
181
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))
186 {
187 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
188 NewStack = PsAllocateCallbackStack(StackSize);
189 AssignedStack = ExAllocatePool(NonPagedPool,
190 sizeof(NTW32CALL_CALLBACK_STACK));
191 AssignedStack->BaseAddress = NewStack;
192 }
193 else
194 {
195 PLIST_ENTRY StackEntry;
196
197 StackEntry = RemoveHeadList(&CallbackStackListHead);
198 KeReleaseSpinLock(&CallbackStackListLock, oldIrql);
199 AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK,
200 ListEntry);
201 NewStack = AssignedStack->BaseAddress;
202 RtlZeroMemory(NewStack, StackSize);
203 }
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);
217
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)
231 {
232 RtlCopyMemory((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA),
233 (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA),
234 sizeof(FX_SAVE_AREA));
235 }
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);
241
242 /*
243 * The callback return will have already restored most of the state we
244 * modified.
245 */
246 KeLowerIrql(DISPATCH_LEVEL);
247 KeAcquireSpinLockAtDpcLevel(&CallbackStackListLock);
248 InsertTailList(&CallbackStackListHead, &AssignedStack->ListEntry);
249 KeReleaseSpinLock(&CallbackStackListLock, PASSIVE_LEVEL);
250 return(CallbackStatus);
251 }
252
253 /* EOF */