summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/ppc/trampoln.s
blob: 5cd50561ac3a0f6b22f912cd07b18f718bdd700b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
//++
//
// Copyright (c) 1993  IBM Corporation and Microsoft Corporation
//
// Module Name:
//
//    trampoln.s
//
// Abstract:
//
//    This module implements the trampoline code necessary to dispatch user
//    mode APCs.
//
// Author:
//
//    Rick Simpson  25-Oct-1993
//
//    based on MIPS version by David N. Cutler (davec) 3-Apr-1990
//
// Environment:
//
//    User mode only.
//
// Revision History:
//
//--

//list(off)
#include "ksppc.h"
//list(on)
                .extern __C_specific_handler
                .extern ..RtlDispatchException
                .extern ..RtlRaiseException
                .extern ..RtlRaiseStatus
                .extern ZwCallbackReturn
                .extern ZwContinue
                .extern ZwRaiseException
                .extern ZwTestAlert

//
// Define layout and length of APC Dispatcher stack frame.
//    N.B.  This must exactly match the computations in KiInitializeUserApc()
//

                .struct 0
ADFrame:        .space  StackFrameHeaderLength
ADContext:      .space  ContextFrameLength
ADTrap:         .space  TrapFrameLength
ADToc:          .long   0
                .space  STK_SLACK_SPACE
                .align  3
ADFrameLength:

                .text

//++
//
// The following code is never executed. Its purpose is to support unwinding
// through the call to the APC dispatcher.
//
//--

        .ydata                                  // scope table -- exception handler
        .align  2
UserApcDispatcherScopeTable:
        .long   1                               // number of scope table entries
        .long   ..KiUserApcDispatcher          // start of scope
        .long   KiUserApcDispatcher.end         // end of scope
        .long   KiUserApcHandler                // filter to decide what to do
        .long   0                               // it always decides to "continue search"
        .text

        FN_TABLE (KiUserApcDispatch,__C_specific_handler,UserApcDispatcherScopeTable)

        DUMMY_ENTRY (KiUserApcDispatch)

        stwu    r.sp, -ADFrameLength (r.sp)
        mflr    r.0
        stw     r.0, ADContext + CxLr (r.sp)
        stw     r.0, ADContext + CxIar (r.sp)
        stw     r.toc, ADToc (r.sp)

        PROLOGUE_END (KiUserApcDispatch)

//++
//
// VOID
// KiUserApcDispatcher (
//    IN PVOID NormalContext,
//    IN PVOID SystemArgument1,
//    IN PVOID SystemArgument2,
//    IN PKNORMAL_ROUTINE NormalRoutine
//    )
//
// Routine Description:
//
//    This routine is entered on return from kernel mode to deliver an APC
//    in user mode. The stack frame for this routine was built when the
//    APC interrupt was processed and contains the entire machine state of
//    the current thread. The specified APC routine is called and then the
//    machine state is restored and execution is continued.
//
//    On entry here, a stack frame as shown above is already addressed
//    via r.1
//
// Arguments:
//
//    r.1 - Stack frame pointer, already set up
//
//    r.3 - Supplies the normal context parameter that was specified when the
//          APC was initialized.
//
//    r.4 - Supplies the first argument that was provied by the executive when
//          the APC was queued.
//
//    r.5 - Supplies the second argument that was provided by the executive
//          when the APC was queued.
//
//    r.6 - Supplies that address of the descriptor for the function that is
//          to be called.
//
// Return Value:
//
//    None.
//
//--

        ALTERNATE_ENTRY (KiUserApcDispatcher)

        lwz     r.0, 0 (r.6)                    // fetch address of APC entry point
        mtlr    r.0                             // move into Link Reg
        lwz     r.2, 4 (r.6)                    // fetch TOC address for APC
        blrl                                    // call specified APC routine

        lwz     r.2, ADToc (r.sp)               // reload our own TOC pointer
        la      r.3, ADContext (r.sp)           // 1st parm = addr of Context Frame
        lwz     r.5, [toc] ZwContinue (r.2)     // fetch ptr to function descriptor
        li      r.4, 1                          // 2nd parm = TRUE (test alert)
        lwz     r.0, 0 (r.5)                    // fetch addr of ZwContinue entry point
        mtlr    r.0                             // move into Link Reg
        lwz     r.2, 4 (r.5)                    // fetch TOC addr for ZwContinue

                                                // this next is done under protest --
                                                //   we are copying MIPS slavishly:
        la      r.12, ADTrap (r.sp)             // "secret" parm = addr of Trap Frame
        blrl                                    // execute system service to continue
        lwz     r.2, ADToc (r.sp)               // reload our own TOC pointer

//
// Unsuccessful completion after attempting to continue execution. Use the
// return status as the exception code, set noncontinuable exception and
// attempt to raise another exception.  Note there is no return from raise
// status.
//

        ori     r.31, r.3, 0                    // save status value
ADloop:
        ori     r.3, r.31, 0                    // set status value
        bl      ..RtlRaiseStatus               // raise exception
        b       ADloop                          // loop on return

        DUMMY_EXIT (KiUserApcDispatcher)
        DUMMY_EXIT (KiUserApcDispatch)

//      SBTTL("User APC Exception Handler")
//++
//
// EXCEPTION_DISPOSITION
// KiUserApcHandler (
//    IN PEXCEPTION_RECORD ExceptionRecord,
//    IN ULONG EstablisherFrame,
//    IN OUT PCONTEXT ContextRecord,
//    IN OUT PDISPATCHER_CONTEXT DispatcherContext
//
// Routine Description:
//
//    This function is called when an exception occurs in an APC routine
//    or one of its dynamic descendents and when an unwind through the
//    APC dispatcher is in progress. If an unwind is in progress, then test
//    alert is called to ensure that all currently queued APCs are executed.
//
// Arguments:
//
//    ExceptionRecord (r.3) - Supplies a pointer to an exception record.
//
//    EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
//       of this exception handler.
//
//    ContextRecord (r.5) - Supplies a pointer to a context record.
//
//    DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
//       record.
//
// Return Value:
//
//    ExceptionContinueSearch is returned as the function value.
//--

        .struct 0
        .space  StackFrameHeaderLength          // canonical stack frame header
        .space  4                               // reserve space for return address
        .space  4                               // reserve space for r.31
        .align  3
HFrameLength:                                   // length of handler frame

        .text

        NESTED_ENTRY (KiUserApcHandler, HFrameLength, 1, 0)

        ori     r.31, r.toc, 0                  // save our TOC value in r.31

        PROLOGUE_END (KiUserApcHandler)

        lwz     r.0, ErExceptionFlags (r.3)     // get exception flags
        andi.   r.0, r.0, EXCEPTION_UNWIND      // check if unwind in progress
        beq     H10                             // if eq, no unwind in progress
        lwz     r.7, [toc] ZwTestAlert (r.toc)  // get addr of function descriptor
        lwz     r.0, 0 (r.7)                    // get entry point address
        mtlr    r.0                             //   into Link Reg
        lwz     r.toc, 4 (r.7)                  // get callee's TOC pointer
        blrl                                    // test for alert pending
        ori     r.toc, r.31, 0                  // reload our own TOC pointer

H10:    li      r.3, ExceptionContinueSearch    // set disposition value

        NESTED_EXIT (KiUserApcHandler, HFrameLength, 1, 0)

//      SBTTL("User Callback Dispatcher")
//++
//
// The following code is never executed. Its purpose is to support unwinding
// through the call to the callback dispatcher.
//
//--

#if 0
        .ydata                                  // scope table -- exception handler
        .align  2
UserCallbackDispatcherScopeTable:
        .long   1                               // number of scope table entries
        .long   ..KiUserCallbackDispatcher      // start of scope
        .long   KiUserCallbackDispatcher.end    // end of scope
        .long   KiUserCallbackHandler           // filter to decide what to do
        .long   0                               // it always decides to "continue search"
        .text

        FN_TABLE (KiUserCallbackDispatch,__C_specific_handler,UserCallbackDispatcherScopeTable)
#endif

        DUMMY_ENTRY (KiUserCallbackDispatch)

        stwu    r.sp, -CkFrameLength(r.sp)
        mflr    r.0
        stw     r.0, CkLr(r.sp)
        stw     r.toc, CkToc(r.sp)

        PROLOGUE_END (KiUserCallbackDispatch)

//++
//
// VOID
// KiUserCallbackDispatcher (
//    VOID
//    )
//
// Routine Description:
//
//    This routine is entered on a callout from kernel mode to execute a
//    user mode callback function. All arguments for this function have
//    been placed on the stack.
//
// Arguments:
//
//    (sp + ApiNumber) - Supplies the API number of the callback function that is
//        executed.
//
//    (sp + Buffer) - Supplies a pointer to the input buffer.
//
//    (sp + Length) - Supplies the input buffer length.
//
// Return Value:
//
//    This function returns to kernel mode.
//
//--

        ALTERNATE_ENTRY(KiUserCallbackDispatcher)

        lwz     r.6, TePeb(r.13)        // get address of PEB
        ori     r.31, r.toc, 0          // save our TOC value in r.31

        lwz     r.5, CkApiNumber(r.1)   // get API number
        lwz     r.6, PeKernelCallbackTable(r.6) // get address of callback table
        lwz     r.3, CkBuffer(r.1)      // get input buffer address
        slwi    r.5, r.5, 2             // compute offset to table entry
        lwz     r.4, CkLength(r.1)      // get input buffer length
        lwzx    r.5, r.5, r.6           // get descriptor for callback routine
        lwz     r.0, 0 (r.5)            // get entry point address
        mtlr    r.0                     //   into link register
        lwz     r.toc, 4 (r.5)          // get callee's TOC pointer
        blrl                            // call specified function

//
// If a return from the callback function occurs, then the output buffer
// address and length are returned as NULL.
//

        ori     r.toc, r.31, 0          // reload our own TOC pointer

        lwz     r.7, [toc] ZwCallbackReturn (r.toc)  // get addr of function descriptor
        ori     r.5, r.3, 0             // set completion status
        li      r.3, 0                  // set zero buffer address
        li      r.4, 0                  // set zero buffer lenfth
        lwz     r.0, 0 (r.7)            // get entry point address
        mtlr    r.0                     //   into Link Reg
        lwz     r.toc, 4 (r.7)          // get callee's TOC pointer
        blrl                            // return to kernel mode

//
// Unsuccessful completion after attempting to return to kernel mode. Use
// the return status as the exception code, set noncontinuable exception and
// attempt to raise another exception. Note there is no return from raise
// status.
//

        ori     r.toc, r.31, 0          // reload our own TOC pointer

        ori     r.31, r.3, 0            // save status value
UCDloop:
        bl      ..RtlRaiseStatus        // raise exception
        ori     r.3, r.31, 0            // set status value
        b       UCDloop                 // loop on return

        DUMMY_EXIT (KiUserCallbackDispatch)

//      SBTTL("User Callback Exception Handler")
//++
//
// EXCEPTION_DISPOSITION
// KiUserCallbackHandler (
//    IN PEXCEPTION_RECORD ExceptionRecord,
//    IN ULONG EstablisherFrame,
//    IN OUT PCONTEXT ContextRecord,
//    IN OUT PDISPATCHER_CONTEXT DispatcherContext
//
// Routine Description:
//
//    This function is called when an exception occurs in a user callback
//    routine or one of its dynamic descendents.
//
// Arguments:
//
//    ExceptionRecord (r.3) - Supplies a pointer to an exception record.
//
//    EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
//       of this exception handler.
//
//    ContextRecord (r.5) - Supplies a pointer to a context record.
//
//    DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
//       record.
//
// Return Value:
//
//    ExceptionContinueSearch is returned as the function value.
//--

        NESTED_ENTRY (KiUserCallbackHandler, HFrameLength, 1, 0)

        ori     r.31, r.toc, 0                  // save our TOC value in r.31

        PROLOGUE_END (KiUserCallbackHandler)

        lwz     r.0, ErExceptionFlags (r.3)     // get exception flags
        andi.   r.0, r.0, EXCEPTION_UNWIND      // check if unwind in progress
        beq     UCH10                           // if eq, no unwind in progress

//
// There is an attempt to unwind through a callback frame. If this were
// allowed, then a kernel callback frame would be abandoned on the kernel
// stack. Force a callback return.
//

        lwz     r.5, ErExceptionCode(r.3)       // get exception code
        li      r.3, 0                          // set zero buffer address
        li      r.4, 0                          // set zero buffer lenfth
        lwz     r.7, [toc]ZwCallbackReturn(r.toc) // get addr of function descriptor
        lwz     r.0, 0 (r.7)                    // get entry point address
        mtlr    r.0                             //   into Link Reg
        lwz     r.toc, 4 (r.7)                  // get callee's TOC pointer
        blrl                                    // return to kernel mode

//
// Unsuccessful completion after attempting to return to kernel mode. Use
// the return status as the exception code, set noncontinuable exception and
// attempt to raise another exception. Note there is no return from raise
// status.
//

        ori     r.toc, r.31, 0          // reload our own TOC pointer

        ori     r.31, r.3, 0            // save status value
UCHloop:
        bl      ..RtlRaiseStatus        // raise exception
        ori     r.3, r.31, 0            // set status value
        b       UCHloop                 // loop on return

UCH10:
        li      r.3, ExceptionContinueSearch    // set disposition value

        NESTED_EXIT (KiUserCallbackHandler, HFrameLength, 1, 0)

//
// Define layout and length of User Exception Dispatcher stack frame.
//    N.B.  This must exactly match the computations in KiDispatchException
//

                .struct 0
EDFrame:        .space  StackFrameHeaderLength
EDExcept:       .space  ExceptionRecordLength
EDContext:      .space  ContextFrameLength
EDToc:          .long   0
                .space  STK_SLACK_SPACE
                .align  3
EDFrameLength:

                .text

//      SBTTL("User Exception Dispatcher")
//++
//
// The following code is never executed. Its purpose is to support unwinding
// through the call to the exception dispatcher.
//
//--

        FN_TABLE (KiUserExceptionDispatch, 0, 0)

        DUMMY_ENTRY (KiUserExceptionDispatch)

        stwu    r.sp, -EDFrameLength (r.sp)     // buy stack frame
        mflr    r.0                             // save linkage
        stw     r.0, EDContext + CxLr (r.sp)    //   regs
        mflr    r.0                             // needed by vunwind code
        stw     r.0, EDContext + CxIar (r.sp)
        stw     r.toc, EDToc (r.sp)

        stw     r.13, EDContext + CxGpr13 (r.sp)  // save non-volatile integer state
        stw     r.14, EDContext + CxGpr14 (r.sp)
        stw     r.15, EDContext + CxGpr15 (r.sp)
        stw     r.16, EDContext + CxGpr16 (r.sp)
        stw     r.17, EDContext + CxGpr17 (r.sp)
        stw     r.18, EDContext + CxGpr18 (r.sp)
        stw     r.19, EDContext + CxGpr19 (r.sp)
        stw     r.20, EDContext + CxGpr20 (r.sp)
        stw     r.21, EDContext + CxGpr21 (r.sp)
        stw     r.22, EDContext + CxGpr22 (r.sp)
        stw     r.23, EDContext + CxGpr23 (r.sp)
        stw     r.24, EDContext + CxGpr24 (r.sp)
        stw     r.25, EDContext + CxGpr25 (r.sp)
        stw     r.26, EDContext + CxGpr26 (r.sp)
        stw     r.27, EDContext + CxGpr27 (r.sp)
        stw     r.28, EDContext + CxGpr28 (r.sp)
        stw     r.29, EDContext + CxGpr29 (r.sp)
        stw     r.30, EDContext + CxGpr30 (r.sp)
        stw     r.31, EDContext + CxGpr31 (r.sp)
        stfd    f.14, EDContext + CxFpr14 (r.sp)  // save non-volatile floating point state
        stfd    f.15, EDContext + CxFpr15 (r.sp)
        stfd    f.16, EDContext + CxFpr16 (r.sp)
        stfd    f.17, EDContext + CxFpr17 (r.sp)
        stfd    f.18, EDContext + CxFpr18 (r.sp)
        stfd    f.19, EDContext + CxFpr19 (r.sp)
        stfd    f.20, EDContext + CxFpr20 (r.sp)
        stfd    f.21, EDContext + CxFpr21 (r.sp)
        stfd    f.22, EDContext + CxFpr22 (r.sp)
        stfd    f.23, EDContext + CxFpr23 (r.sp)
        stfd    f.24, EDContext + CxFpr24 (r.sp)
        stfd    f.25, EDContext + CxFpr25 (r.sp)
        stfd    f.26, EDContext + CxFpr26 (r.sp)
        stfd    f.27, EDContext + CxFpr27 (r.sp)
        stfd    f.28, EDContext + CxFpr28 (r.sp)
        stfd    f.29, EDContext + CxFpr29 (r.sp)
        stfd    f.30, EDContext + CxFpr30 (r.sp)
        stfd    f.31, EDContext + CxFpr31 (r.sp)

        PROLOGUE_END (KiUserExceptionDispatch)

//++
//
// VOID
// KiUserExceptionDispatcher (
//    IN PEXCEPTION_RECORD ExceptionRecord,
//    IN PCONTEXT ContextRecord
//    )
//
// Routine Description:
//
//    This routine is entered on return from kernel mode to dispatch a user
//    mode exception. If a frame based handler handles the exception, then
//    the execution is continued. Else last chance processing is performed.
//
// Arguments:
//
//    r.3 - Supplies a pointer to an exception record.
//
//    r.4 - Supplies a pointer to a context record.
//
// Return Value:
//
//    None.
//
//--

        ALTERNATE_ENTRY(KiUserExceptionDispatcher)

        bl      ..RtlDispatchException         // attempt to dispatch the exception

//
// If the return status is TRUE, then the exception was handled and execution
// should be continued with the NtContinue service in case the context was
// changed. If the return status is FALSE, then the exception was not handled
// and NtRaiseException is called to perform last chance exception processing.
//

        cmpwi   r.3, 0                          // compare return value to FALSE
        beq     ED10                            // if eq, perform last chance processing

//
// Continue execution.
//

        lwz     r.5, [toc] ZwContinue (r.2)     // load pointer to function descriptor
        la      r.3, EDContext (r.sp)           // set addr of context frame
        lwz     r.0, 0 (r.5)                    // load entry point address
        mtlr    r.0                             // move into Link Reg
        li      r.4, 0                          // set test alert argument false
        lwz     r.2, 4 (r.5)                    // load ZwContinue's TOC pointer
        blrl                                    // execute system service to continue
        lwz     r.2, EDToc (r.sp)               // reload our own TOC address
        b       ED20                            // join common code

//
// Last chance processing.
//

ED10:
        lwz     r.6, [toc] ZwRaiseException (r.2) // load pointer to function descriptor
        la      r.3, EDExcept (r.sp)            // set address of exception record
        lwz     r.0, 0 (r.6)                    // load entry point address
        mtlr    r.0                             //   into link reg
        la      r.4, EDContext (r.sp)           // set address of context frame
        li      r.5, 0                          // set first chance FALSE
        lwz     r.toc, 4 (r.6)                  // load callee's TOC addr
        blrl                                    // perform last chance processing
        lwz     r.toc, EDToc (r.sp)             // reload our own TOC address

//
// Common code for nonsuccessful completion of the continue or last chance
// service. Use the return status as the exception code, set noncontinuable
// exception and attempt to raise another exception. Note the stack grows
// and eventually this loop will end.
//

ED20:                                           // status value is in r.3
        la      r.4, EDExcept (r.sp)            // point to our exception record
        bl      ..KipUserExceptionDispatcherLoop // call subroutine below

        DUMMY_EXIT (KiUserExceptionDispatch)

//++
//
// VOID
// KiUserExceptionDispatcherLoop (
//    IN ULONG ExceptionStatus
//    )
//
// Routine Description:
//
//    This routine builds an Exception Record and calls RtlRaiseException.
//    On an unsuccessful return, it calls itself recursively; eventually
//    this will terminate when the stack fills up.
//
// Arguments:
//
//    r.3 - Status value
//
// Return Value:
//
//    None.  (Does not return.)
//
//--

//
// Stack frame layout for KipUserExceptionDispatchLoop
//

                .struct 0
LFrame:         .space  StackFrameHeaderLength
LExcept:        .space  ExceptionRecordLength
LOldExcept:     .long   0
LSavedLR:       .long   0
                .align  3
LFrameLength:

                .text

        NESTED_ENTRY (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0)

        PROLOGUE_END (KipUserExceptionDispatcherLoop)

        stw     r.4, LOldExcept (r.sp)          // save incoming exception rec addr
        la      r.5, LExcept (r.sp)             // point to Exception Record
        stw     r.3, ErExceptionCode (r.5)      // fill in exception code (incoming status)
        li      r.6, EXCEPTION_NONCONTINUABLE   // set non-continuable flag
        stw     r.6, ErExceptionFlags (r.5)
        stw     r.4, ErExceptionRecord (r.5)    // set addr of prev. exception record
        li      r.0, 0                          // set number of parameters
        stw     r.0, ErNumberParameters (r.5)   //   to 0
        ori     r.3, r.5, 0                     // load 1st parameter pointer
        bl      ..RtlRaiseException            // raise an exception

        lwz     r.4, LOldExcept (r.sp)          // should not return, but if so:
        bl      ..KipUserExceptionDispatcherLoop // keep doing this in a loop

        NESTED_EXIT (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0)

//++
//
// NTSTATUS
// KiRaiseUserExceptionDispatcher (
//    IN NTSTATUS ExceptionCode
//    )
//
// Routine Description:
//
//    This routine is entered on return from kernel mode to raise a user
//    mode exception.
//
// Arguments:
//
//    r3 - Supplies the status code to be raised.
//
// Return Value:
//
//    ExceptionCode
//
//--

//
// N.B. This function is not called in the typical way. Instead of a normal
// subroutine call to the nested entry point above, the alternate entry point
// address below is stuffed into the Iar address of the trap frame. Thus when
// the kernel returns from the trap, the following code is executed directly.
//

                .struct 0
ruedFrame:      .space  StackFrameHeaderLength
ruedExr:        .space  ExceptionRecordLength
ruedR3:         .space  4
ruedLr:         .space  4
                .align  3
ruedFrameLength:

        SPECIAL_ENTRY(KiRaiseUserExceptionDispatcher)

        mflr    r4                              // get return address (also exception address)
        stwu    sp,-ruedFrameLength(sp)         // allocate stack frame
        li      r0,0                            // get a 0
        stw     r4,ruedLr(sp)                   // save return address

        PROLOGUE_END(KiRaiseUserExceptionDispatcher)

        stw     r3,ruedR3(sp)                   // save function return status
        stw     r3,ErExceptionCode+ruedExr(sp)  // set exception code
        la      r3,ruedExr(sp)                  // compute exception record address
        lwz     r4,ruedLr(sp)                   // get exception address
        stw     r0,ErExceptionFlags(r3)         // set exception flags
        stw     r0,ErExceptionRecord(r3)        // set exception record
        stw     r0,ErNumberParameters(r3)       // set number of parameters
        stw     r4,ErExceptionAddress(r3)       // set exception address

        bl      ..RtlRaiseException             // attempt to raise the exception

        lwz     r3,ruedR3(sp)                   // restore function status

        NESTED_EXIT (KiRaiseUserExceptionDispatcher, ruedFrameLength, 0, 0)