Fix RtlpGetStackLimits to get the right limits if called in kernel-mode (separated...
[reactos.git] / reactos / ntoskrnl / rtl / i386 / seh.s
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * PURPOSE: Runtime library exception support for IA-32
6 * FILE: ntoskrnl/rtl/i386/seh.s
7 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * NOTES: This file is shared with lib/msvcrt/except/seh.s.
9 * Please keep them in sync.
10 */
11
12 #include <ndk/asm.h>
13
14 #define ExceptionContinueExecution 0
15 #define ExceptionContinueSearch 1
16 #define ExceptionNestedException 2
17 #define ExceptionCollidedUnwind 3
18
19 #define EXCEPTION_NONCONTINUABLE 0x01
20 #define EXCEPTION_UNWINDING 0x02
21 #define EXCEPTION_EXIT_UNWIND 0x04
22 #define EXCEPTION_STACK_INVALID 0x08
23 #define EXCEPTION_NESTED_CALL 0x10
24 #define EXCEPTION_TARGET_UNWIND 0x20
25 #define EXCEPTION_COLLIDED_UNWIND 0x40
26
27 #define EXCEPTION_UNWIND_MODE \
28 ( EXCEPTION_UNWINDING \
29 | EXCEPTION_EXIT_UNWIND \
30 | EXCEPTION_TARGET_UNWIND \
31 | EXCEPTION_COLLIDED_UNWIND)
32
33 #define EREC_CODE 0x00
34 #define EREC_FLAGS 0x04
35 #define EREC_RECORD 0x08
36 #define EREC_ADDRESS 0x0C
37 #define EREC_NUMPARAMS 0x10
38 #define EREC_INFO 0x14
39
40 #define TRYLEVEL_NONE -1
41 #define TRYLEVEL_INVALID -2
42
43 #define ER_STANDARDESP -0x08
44 #define ER_EPOINTERS -0x04
45 #define ER_PREVFRAME 0x00
46 #define ER_HANDLER 0x04
47 #define ER_SCOPETABLE 0x08
48 #define ER_TRYLEVEL 0x0C
49 #define ER_EBP 0x10
50
51 #define ST_TRYLEVEL 0x00
52 #define ST_FILTER 0x04
53 #define ST_HANDLER 0x08
54
55 #define CONTEXT_EDI 0x9C
56 #define CONTEXT_EBX 0xA4
57 #define CONTEXT_EIP 0xB8
58
59 .globl __local_unwind2
60 .globl __except_handler3
61
62 // EAX = value to print
63 _do_debug:
64 pushal
65 pushl %eax
66 call _MsvcrtDebug@4
67 popal
68 ret
69
70 #define LU2_TRYLEVEL 0x08
71 #define LU2_REGFRAME 0x04
72
73 //
74 // void
75 // _local_unwind2(PEXCEPTION_REGISTRATION RegistrationFrame,
76 // LONG TryLevel)
77 //
78 // Parameters:
79 // [EDX+08h] - PEXCEPTION_REGISTRATION RegistrationFrame
80 // [EDX+04h] - LONG TryLevel
81 // Registers:
82 // EBP - EBP of call frame we are unwinding
83 // Returns:
84 // Nothing
85 // Notes:
86 // Run all termination handlers for a call frame from the current
87 // try-level up to (but not including) the given stop try-level.
88 __local_unwind2:
89 // Setup our call frame so we can access parameters using EDX
90 //pushl %ebp
91 movl %esp, %edx
92
93 // FIXME: Setup an EXCEPTION_REGISTRATION entry to protect the
94 // unwinding in case something goes wrong
95
96 .lu2_next_scope:
97
98 // Keep a pointer to the exception registration in EBX
99 movl LU2_REGFRAME(%edx), %ebx
100
101 // If we have reached the end of the chain or we're asked to stop here
102 // by the caller then exit
103 movl ER_TRYLEVEL(%ebx), %eax
104
105 cmpl $-1, %eax
106 je .lu2_done
107
108 cmpl LU2_TRYLEVEL(%edx), %eax
109 je .lu2_done
110
111 // Keep a pointer to the scopetable in ESI
112 movl ER_SCOPETABLE(%ebx), %esi
113
114 // Compute the offset of the entry in the scopetable that describes
115 // the scope that is to be unwound. Put the offset in EDI.
116 movl ST_TRYLEVEL(%esi), %edi
117 lea (%edi, %edi, 2), %edi
118 shll $2, %edi
119 addl %esi, %edi
120
121 // If this is not a termination handler then skip it
122 cmpl $0, ST_FILTER(%edi)
123 jne .lu2_next_scope
124
125 // Save the previous try-level in the exception registration structure
126 movl ST_TRYLEVEL(%edi), %eax
127 movl %eax, ER_TRYLEVEL(%ebx)
128
129 // Fetch the address of the termination handler
130 movl ST_HANDLER(%edi), %eax
131
132 // Termination handlers may trash all registers so save the
133 // important ones and then call the handler
134 pushl %edx
135 call *%eax
136
137 // Get our base pointer back
138 popl %edx
139
140 jmp .lu2_next_scope
141
142 .lu2_done:
143
144 // FIXME: Tear down the EXCEPTION_REGISTRATION entry setup to protect
145 // the unwinding
146
147 //movl %esi, %esp
148 //popl %ebp
149 ret
150
151 #define EH3_DISPCONTEXT 0x14
152 #define EH3_CONTEXT 0x10
153 #define EH3_REGFRAME 0x0C
154 #define EH3_ERECORD 0x08
155
156 // Parameters:
157 // [ESP+14h] - PVOID DispatcherContext
158 // [ESP+10h] - PCONTEXT Context
159 // [ESP+0Ch] - PEXCEPTION_REGISTRATION RegistrationFrame
160 // [ESP+08h] - PEXCEPTION_RECORD ExceptionRecord
161 // Registers:
162 // Unknown
163 // Returns:
164 // EXCEPTION_DISPOSITION - How this handler handled the exception
165 // Notes:
166 // Try to find an exception handler that will handle the exception.
167 // Traverse the entries in the scopetable that is associated with the
168 // exception registration passed as a parameter to this function.
169 // If an exception handler that will handle the exception is found, it
170 // is called and this function never returns
171 __except_handler3:
172 // Setup our call frame so we can access parameters using EBP
173 pushl %ebp // Standard ESP in frame (considered part of EXCEPTION_REGISTRATION)
174 movl %esp, %ebp
175
176 // Don't trust the direction flag to be cleared
177 cld
178
179 // Either we're called to handle an exception or we're called to unwind
180 movl EH3_ERECORD(%ebp), %eax
181 testl $EXCEPTION_UNWIND_MODE, EREC_FLAGS(%eax)
182 jnz .eh3_unwind
183
184 // Keep a pointer to the exception registration in EBX
185 movl EH3_REGFRAME(%ebp), %ebx
186
187 // Build an EXCEPTION_POINTERS structure on the stack and store it's
188 // address in the EXCEPTION_REGISTRATION structure
189 movl EH3_CONTEXT(%esp), %eax
190 pushl %ebx // Registration frame
191 pushl %eax // Context
192 movl %esp, ER_EPOINTERS(%ebx) // Pointer to EXCEPTION_REGISTRATION on the stack
193
194 // Keep current try-level in EDI
195 movl ER_TRYLEVEL(%ebx), %edi
196
197 // Keep a pointer to the scopetable in ESI
198 movl ER_SCOPETABLE(%ebx), %esi
199
200 .eh3_next_scope:
201
202 // If we have reached the end of the chain then exit
203 cmpl $-1, %edi
204 je .eh3_search
205
206 // Compute the offset of the entry in the scopetable and store
207 // the absolute address in EAX
208 lea (%edi, %edi, 2), %eax
209 shll $2, %eax
210 addl %esi, %eax
211
212 // Fetch the address of the filter routine
213 movl ST_FILTER(%eax), %eax
214
215 // If this is a termination handler then skip it
216 cmpl $0, %eax
217 je .eh3_continue
218
219 // Filter routines may trash all registers so save the important
220 // ones before restoring the call frame ebp and calling the handler
221 pushl %ebp
222 pushl %edi // Stop try-level
223 lea ER_EBP(%ebx), %ebp
224 call *%eax
225 popl %edi // Stop try-level
226 popl %ebp
227
228 // Reload EBX with registration frame address
229 movl EH3_REGFRAME(%ebp), %ebx
230
231 // Be more flexible here by checking if the return value is less than
232 // zero, equal to zero, or larger than zero instead of the defined
233 // values:
234 // -1 (EXCEPTION_CONTINUE_EXECUTION)
235 // 0 (EXCEPTION_CONTINUE_SEARCH)
236 // +1 (EXCEPTION_EXECUTE_HANDLER)
237 orl %eax, %eax
238 jz .eh3_continue
239 js .eh3_dismiss
240
241 // Filter returned: EXCEPTION_EXECUTE_HANDLER
242
243 // Ask the OS to perform global unwinding.
244 pushl %edi // Save stop try-level
245 pushl %ebx // Save registration frame address
246 pushl %ebx // Registration frame address
247 call __global_unwind2
248 popl %eax // Remove parameter to __global_unwind2
249 popl %ebx // Restore registration frame address
250 popl %edi // Restore stop try-level
251
252 // Change the context structure so _except_finish is called in the
253 // correct context since we return ExceptionContinueExecution.
254 movl EH3_CONTEXT(%ebp), %eax
255
256 movl %edi, CONTEXT_EDI(%eax) // Stop try-level
257 movl %ebx, CONTEXT_EBX(%eax) // Registration frame address
258 movl $_except_finish, CONTEXT_EIP(%eax)
259
260 movl $ExceptionContinueExecution, %eax
261 jmp .eh3_return
262
263 // Filter returned: EXCEPTION_CONTINUE_SEARCH
264 .eh3_continue:
265
266 // Reload ESI because the filter routine may have trashed it
267 movl ER_SCOPETABLE(%ebx), %esi
268
269 // Go one try-level closer to the top
270 lea (%edi, %edi, 2), %edi
271 shll $2, %edi
272 addl %esi, %edi
273 movl ST_TRYLEVEL(%edi), %edi
274
275 jmp .eh3_next_scope
276
277 // Filter returned: EXCEPTION_CONTINUE_EXECUTION
278 // Continue execution like nothing happened
279 .eh3_dismiss:
280 movl $ExceptionContinueExecution, %eax
281 jmp .eh3_return
282
283 // Tell the OS to search for another handler that will handle the exception
284 .eh3_search:
285
286 movl $ExceptionContinueSearch, %eax
287 jmp .eh3_return
288
289 // Perform local unwinding
290 .eh3_unwind:
291
292 movl $ExceptionContinueSearch, %eax
293 testl $EXCEPTION_TARGET_UNWIND, EREC_FLAGS(%eax)
294 jnz .eh3_return
295
296 // Save some important registers
297 pushl %ebp
298
299 lea ER_EBP(%ebx), %ebp
300 pushl $-1
301 pushl %ebx
302 call __local_unwind2
303 addl $8, %esp
304
305 // Restore some important registers
306 popl %ebp
307
308 movl $ExceptionContinueSearch, %eax
309
310 // Get me out of here
311 .eh3_return:
312
313 movl %ebp, %esp
314 popl %ebp
315 ret
316
317 // Parameters:
318 // None
319 // Registers:
320 // EBX - Pointer to exception registration structure
321 // EDI - Stop try-level
322 // Returns:
323 // -
324 // Notes:
325 // -
326 _except_finish:
327
328 // Setup EBP for the exception handler. By doing this the exception
329 // handler can access local variables as normal
330 lea ER_EBP(%ebx), %ebp
331
332 // Save some important registers
333 pushl %ebp
334 pushl %ebx
335 pushl %edi
336
337 // Stop try-level
338 pushl %edi
339
340 // Pointer to exception registration structure
341 pushl %ebx
342 call __local_unwind2
343 addl $8, %esp
344
345 // Restore some important registers
346 popl %edi
347 popl %ebx
348 popl %ebp
349
350 // Keep a pointer to the scopetable in ESI
351 movl ER_SCOPETABLE(%ebx), %esi
352
353 // Compute the offset of the entry in the scopetable and store
354 // the absolute address in EDI
355 lea (%edi, %edi, 2), %edi
356 shll $2, %edi
357 addl %esi, %edi
358
359 // Set the current try-level to the previous try-level and call
360 // the exception handler
361 movl ST_TRYLEVEL(%edi), %eax
362 movl %eax, ER_TRYLEVEL(%ebx)
363 movl ST_HANDLER(%edi), %eax
364
365 call *%eax
366
367 // We should never get here
368 ret
369
370 .intel_syntax noprefix
371 .globl _RtlpGetStackLimits@8
372 _RtlpGetStackLimits@8:
373
374 /* Get the current thread */
375 mov eax, [fs:KPCR_CURRENT_THREAD]
376
377 /* Get the stack limits */
378 mov ecx, [eax+KTHREAD_STACK_LIMIT]
379 mov edx, [eax+KTHREAD_INITIAL_STACK]
380 sub edx, SIZEOF_FX_SAVE_AREA
381
382 /* Return them */
383 mov eax, [esp+4]
384 mov [eax], ecx
385
386 mov eax, [esp+8]
387 mov [eax], edx
388
389 /* return */
390 ret 8
391