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