- Remove usercall.c from portable part of Ke and add it to ke\i386. The implementatio...
[reactos.git] / reactos / ntoskrnl / ke / i386 / usercall.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/usercall.c
5 * PURPOSE: User-mode Callout Mechanisms (APC and Win32K Callbacks)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/debug.h>
14
15 /* PRIVATE FUNCTIONS *********************************************************/
16
17 _SEH_DEFINE_LOCALS(KiCopyInfo)
18 {
19 volatile EXCEPTION_RECORD SehExceptRecord;
20 };
21
22 _SEH_FILTER(KiCopyInformation2)
23 {
24 _SEH_ACCESS_LOCALS(KiCopyInfo);
25
26 /* Copy the exception records and return to the handler */
27 RtlMoveMemory((PVOID)&_SEH_VAR(SehExceptRecord),
28 _SEH_GetExceptionPointers()->ExceptionRecord,
29 sizeof(EXCEPTION_RECORD));
30 return EXCEPTION_EXECUTE_HANDLER;
31 }
32
33 /*++
34 * @name KiInitializeUserApc
35 *
36 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
37 *
38 * @param Reserved
39 * Pointer to the Exception Frame on non-i386 builds.
40 *
41 * @param TrapFrame
42 * Pointer to the Trap Frame.
43 *
44 * @param NormalRoutine
45 * Pointer to the NormalRoutine to call.
46 *
47 * @param NormalContext
48 * Pointer to the context to send to the Normal Routine.
49 *
50 * @param SystemArgument[1-2]
51 * Pointer to a set of two parameters that contain untyped data.
52 *
53 * @return None.
54 *
55 * @remarks None.
56 *
57 *--*/
58 VOID
59 NTAPI
60 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
61 IN PKTRAP_FRAME TrapFrame,
62 IN PKNORMAL_ROUTINE NormalRoutine,
63 IN PVOID NormalContext,
64 IN PVOID SystemArgument1,
65 IN PVOID SystemArgument2)
66 {
67 CONTEXT Context;
68 ULONG_PTR Stack;
69 ULONG Size;
70 EXCEPTION_RECORD SehExceptRecord;
71 _SEH_DECLARE_LOCALS(KiCopyInfo);
72
73 /* Don't deliver APCs in V86 mode */
74 if (TrapFrame->EFlags & X86_EFLAGS_VM) return;
75
76 /* Save the full context */
77 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
78 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
79
80 /* Protect with SEH */
81 _SEH_TRY
82 {
83 /* Sanity check */
84 ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
85
86 /* Get the aligned size */
87 Size = ((sizeof(CONTEXT) + 3) & ~3) + 4 * sizeof(ULONG_PTR);
88 Stack = (Context.Esp & ~3) - Size;
89
90 /* Probe and copy */
91 ProbeForWrite((PVOID)Stack, Size, 4);
92 RtlMoveMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))),
93 &Context,
94 sizeof(CONTEXT));
95
96 /* Run at APC dispatcher */
97 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
98 TrapFrame->HardwareEsp = Stack;
99
100 /* Setup Ring 3 state */
101 TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
102 TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
103 TrapFrame->SegDs = KGDT_R3_DATA | RPL_MASK;
104 TrapFrame->SegEs = KGDT_R3_DATA | RPL_MASK;
105 TrapFrame->SegGs = 0;
106
107 /* Sanitize EFLAGS */
108 TrapFrame->EFlags = Context.EFlags & EFLAGS_USER_SANITIZE;
109 TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
110
111 /* Check if user-mode has IO privileges */
112 if (KeGetCurrentThread()->Iopl)
113 {
114 /* Enable them*/
115 TrapFrame->EFlags |= (0x3000);
116 }
117
118 /* Setup the stack */
119 *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine;
120 *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext;
121 *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1;
122 *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2;
123 }
124 _SEH_EXCEPT(KiCopyInformation2)
125 {
126 /* Dispatch the exception */
127 _SEH_VAR(SehExceptRecord).ExceptionAddress = (PVOID)TrapFrame->Eip;
128 KiDispatchException(&SehExceptRecord,
129 ExceptionFrame,
130 TrapFrame,
131 UserMode,
132 TRUE);
133 }
134 _SEH_END;
135 }
136
137 /* PUBLIC FUNCTIONS **********************************************************/
138
139 /*
140 * @implemented
141 */
142 NTSTATUS
143 NTAPI
144 KeUserModeCallback(IN ULONG RoutineIndex,
145 IN PVOID Argument,
146 IN ULONG ArgumentLength,
147 OUT PVOID *Result,
148 OUT PULONG ResultLength)
149 {
150 ULONG_PTR NewStack, OldStack;
151 PULONG UserEsp;
152 NTSTATUS CallbackStatus = STATUS_SUCCESS;
153 PEXCEPTION_REGISTRATION_RECORD ExceptionList;
154 PTEB Teb;
155 ULONG GdiBatchCount = 0;
156 ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
157 ASSERT(KeGetPreviousMode() == UserMode);
158
159 /* Get the current user-mode stack */
160 UserEsp = KiGetUserModeStackAddress();
161 OldStack = *UserEsp;
162
163 /* Enter a SEH Block */
164 _SEH_TRY
165 {
166 /* Calculate and align the stack size */
167 NewStack = (OldStack - ArgumentLength) & ~3;
168
169 /* Make sure it's writable */
170 ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
171 ArgumentLength + 6 * sizeof(ULONG_PTR),
172 sizeof(CHAR));
173
174 /* Copy the buffer into the stack */
175 RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
176
177 /* Write the arguments */
178 NewStack -= 24;
179 *(PULONG)NewStack = 0;
180 *(PULONG)(NewStack + 4) = RoutineIndex;
181 *(PULONG)(NewStack + 8) = (NewStack + 24);
182 *(PULONG)(NewStack + 12) = ArgumentLength;
183
184 /* Save the exception list */
185 Teb = KeGetCurrentThread()->Teb;
186 ExceptionList = Teb->Tib.ExceptionList;
187
188 /* Jump to user mode */
189 *UserEsp = NewStack;
190 CallbackStatus = KiCallUserMode(Result, ResultLength);
191 if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
192 {
193 /* Only restore the exception list if we didn't crash in ring 3 */
194 Teb->Tib.ExceptionList = ExceptionList;
195 CallbackStatus = STATUS_SUCCESS;
196 }
197 else
198 {
199 /* Otherwise, pop the stack */
200 OldStack = *UserEsp;
201 }
202
203 /* Read the GDI Batch count */
204 GdiBatchCount = Teb->GdiBatchCount;
205 }
206 _SEH_HANDLE
207 {
208 /* Get the SEH exception */
209 CallbackStatus = _SEH_GetExceptionCode();
210 }
211 _SEH_END;
212 if (!NT_SUCCESS(CallbackStatus)) return CallbackStatus;
213
214 /* Check if we have GDI Batch operations */
215 if (GdiBatchCount)
216 {
217 /* Shouldn't happen in ROS yet */
218 ASSERT(FALSE);
219 }
220
221 /* Restore stack and return */
222 *UserEsp = OldStack;
223 return CallbackStatus;
224 }
225
226 /* EOF */