summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/alpha/trampoln.s
blob: b37d471e744d4bfe4613933e2cc4c22db3764718 (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
//      TITLE("Trampoline Code For User Mode APC and Exception Dispatching")
//++
//
// Copyright (c) 1990  Microsoft Corporation
// Copyright (c) 1992  Digital Equipment Corporation
//
// Module Name:
//
//    trampoln.s
//
// Abstract:
//
//    This module implements the trampoline code necessary to dispatch user
//    mode APCs and exceptions.
//
// Author:
//
//    David N. Cutler (davec) 3-Apr-1990
//
// Environment:
//
//    User mode only.
//
// Revision History:
//
//    Thomas Van Baak (tvb) 11-May-1992
//
//        Adapted for Alpha AXP.
//
//--

#include "ksalpha.h"

//
// Define length of exception dispatcher stack frame.
//

#define ExceptionDispatcherFrameLength (ExceptionRecordLength + ContextFrameLength)

        SBTTL("User APC Dispatcher")
//++
//
// The following code is never executed. Its purpose is to support unwinding
// through the call to the APC dispatcher.
//
//--

//
// N.B. This function specifies its own private exception handler.
//
        EXCEPTION_HANDLER(KiUserApcHandler)

        NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero);

        .set    noreorder
        .set    noat
        stq     sp, CxIntSp(sp)         // save stack pointer
        stq     ra, CxIntRa(sp)         // save return address
        stq     ra, CxFir(sp)           // set continuation address
        stq     fp, CxIntFp(sp)         // save integer register fp
        stq     gp, CxIntGp(sp)         // save integer register gp

        stq     s0, CxIntS0(sp)         // save integer registers s0 - s5
        stq     s1, CxIntS1(sp)         //
        stq     s2, CxIntS2(sp)         //
        stq     s3, CxIntS3(sp)         //
        stq     s4, CxIntS4(sp)         //
        stq     s5, CxIntS5(sp)         //

        stt     f2, CxFltF2(sp)         // save floating registers f2 - f9
        stt     f3, CxFltF3(sp)         //
        stt     f4, CxFltF4(sp)         //
        stt     f5, CxFltF5(sp)         //
        stt     f6, CxFltF6(sp)         //
        stt     f7, CxFltF7(sp)         //
        stt     f8, CxFltF8(sp)         //
        stt     f9, CxFltF9(sp)         //

        mov     sp, fp                  // set frame pointer
        .set    at
        .set    reorder

        PROLOGUE_END

//++
//
// 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 context 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.
//
// Arguments:
//
//    a0 - Supplies the normal context parameter that was specified when the
//       APC was initialized.
//
//    a1 - Supplies the first argument that was provided by the executive when
//       the APC was queued.
//
//    a2 - Supplies the second argument that was provided by the executive
//       when the APC was queued.
//
//    a3 - Supplies the address of the function that is to be called.
//
//    N.B. Register sp supplies a pointer to a context frame.
//
//    N.B. Register fp supplies the same value as sp and is used as a frame
//       pointer.
//
// Return Value:
//
//    None.
//
//--

//
// 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 Fir address of the trap frame. Thus when
// the kernel returns from the trap, the following code is executed directly.
//
        ALTERNATE_ENTRY(KiUserApcDispatcher)

        jsr     ra, (a3)                // call specified APC routine

        mov     fp, a0                  // set address of context frame
        ldil    a1, TRUE                // set test alert argument true
        bsr     ra, ZwContinue          // execute system service to continue
        mov     v0, s0                  // save status value

//
// 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.
//

10:     mov     s0, a0                  // set status value
        bsr     ra, RtlRaiseStatus      // raise exception
        br      zero, 10b               // loop on return

        .end    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, or 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 (a0) - Supplies a pointer to an exception record.
//
//    EstablisherFrame (a1) - Supplies the frame pointer of the establisher
//       of this exception handler.
//
//    ContextRecord (a2) - Supplies a pointer to a context record.
//
//    DispatcherContext (a3) - Supplies a pointer to the dispatcher context
//       record.
//
// Return Value:
//
//    ExceptionContinueSearch is returned as the function value.
//
//--

        .struct 0
HdRa:   .space  8                       // saved return address
        .space  1 * 8                   // required for 16-byte stack alignment
HandlerFrameLength:                     // length of handler frame

        NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero)

        lda     sp, -HandlerFrameLength(sp)     // allocate stack frame
        stq     ra, HdRa(sp)                    // save return address

        PROLOGUE_END

//
// The following code is equivalent to:
//
//      EXCEPTION_DISPOSITION
//      KiUserApcHandler(IN PEXCEPTION_RECORD ExceptionRecord)
//      {
//          if (IS_UNWINDING(ExceptionRecord->ExceptionFlags)) {
//              NtTestAlert();
//          }
//          return ExceptionContinueSearch
//      }
//

        ldl     t0, ErExceptionFlags(a0)        // get exception flags
        and     t0, EXCEPTION_UNWIND, t0        // check if unwind in progress
        beq     t0, 10f                         // if eq, no unwind in progress

        bsr     ra, ZwTestAlert                 // test for alert pending

10:     ldil    v0, ExceptionContinueSearch     // set disposition value
        ldq     ra, HdRa(sp)                    // restore return address
        lda     sp, HandlerFrameLength(sp)      // deallocate stack frame
        ret     zero, (ra)                      // return

        .end    KiUserApcHandler


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

        NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero);
.set noreorder
        stq     sp, CkSp(sp)
        stq     ra, CkRa(sp)
.set reorder
        PROLOGUE_END
//++
//
// 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 + 16) - Supplies a value of zero for alignment.
//
//    (sp + 24) - Supplies the API number of the callback function that is
//        executed.
//
//    (sp + 32) - Supplies a pointer to the input buffer.
//
//    (sp + 40) - Supplies the input buffer length.
//
// Return Value:
//
//    This function returns to kernel mode.
//
//--

        ALTERNATE_ENTRY(KiUserCallbackDispatcher)

        ldl     a0, CkBuffer(sp)        // get input buffer address
        ldl     a1, CkLength(sp)        // get input buffer length
        ldl     t0, CkApiNumber(sp)     // get API number
        GET_THREAD_ENVIRONMENT_BLOCK    // get TEB in v0
        ldl     t5, TePeb(v0)           // get PEB in t5
        ldl     t2, PeKernelCallbackTable(t5)  // get address of callback table
        s4addl  t0, t2, t3              // get address of callback
        ldl     t4, 0(t3)               // get callback pointer
        jsr     ra, (t4)                // call specified function

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

        bis     zero,zero,a0            // set zero buffer address
        bis     zero,zero,a1            // set zero buffer length
        bis     v0, zero, a2            // set completion status
        bsr     ra, ZwCallbackReturn    // 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.
//

        bis     v0, zero, s0            // save status value
10:     bis     s0, zero, a0            // set status value
        bsr     ra, RtlRaiseStatus      // raise exception
        br      zero, 10b               // loop on return

        .end    KiUserCallbackDispatch

        SBTTL("User Exception Dispatcher")
//++
//
// The following code is never executed. Its purpose is to support unwinding
// through the call to the exception dispatcher.
//
// When reverse executed, this prologue will restore all integer registers,
// rather than just the non-volatile registers. This is necessary for proper
// unwinding through the call to the exception dispatcher when non-standard
// calls have been used in frames at or above the exception frame.  Non-leaf
// functions using a non-standard call are allowed to save the return address
// register in another integer register instead of on the stack.
//
//--

        NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrameLength, zero);

        .set    noreorder
        .set    noat
        stq     sp, CxIntSp(sp)         // save stack pointer
        stq     ra, CxIntRa(sp)         // save return address
        stq     ra, CxFir(sp)           // set continuation address

        stq     v0, CxIntV0(sp)         // save integer register v0
        stq     t0, CxIntT0(sp)         // save integer registers t0 - t6
        stq     t1, CxIntT1(sp)         //
        stq     t2, CxIntT2(sp)         //
        stq     t3, CxIntT3(sp)         //
        stq     t4, CxIntT4(sp)         //
        stq     t5, CxIntT5(sp)         //
        stq     t6, CxIntT6(sp)         //
        stq     t7, CxIntT7(sp)         //

        stq     s0, CxIntS0(sp)         // save integer registers s0 - s5
        stq     s1, CxIntS1(sp)         //
        stq     s2, CxIntS2(sp)         //
        stq     s3, CxIntS3(sp)         //
        stq     s4, CxIntS4(sp)         //
        stq     s5, CxIntS5(sp)         //
        stq     fp, CxIntFp(sp)         // save integer register fp

        stq     a0, CxIntA0(sp)         // save integer registers a0 - a5
        stq     a1, CxIntA1(sp)         //
        stq     a2, CxIntA2(sp)         //
        stq     a3, CxIntA3(sp)         //
        stq     a4, CxIntA4(sp)         //
        stq     a5, CxIntA5(sp)         //

        stq     t8, CxIntT8(sp)         // save integer registers t8 - t11
        stq     t9, CxIntT9(sp)         //
        stq     t10, CxIntT10(sp)       //
        stq     t11, CxIntT11(sp)       //

        stq     t12, CxIntT12(sp)       // save integer register t12
        stq     AT, CxIntAt(sp)         // save integer register AT
        stq     gp, CxIntGp(sp)         // save integer register gp

        stt     f2, CxFltF2(sp)         // save floating registers f2 - f9
        stt     f3, CxFltF3(sp)         //
        stt     f4, CxFltF4(sp)         //
        stt     f5, CxFltF5(sp)         //
        stt     f6, CxFltF6(sp)         //
        stt     f7, CxFltF7(sp)         //
        stt     f8, CxFltF8(sp)         //
        stt     f9, CxFltF9(sp)         //

        mov     sp, fp                  // set frame pointer
        .set    at
        .set    reorder

        PROLOGUE_END

//++
//
// 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. Otherwise last chance processing is performed.
//
// Arguments:
//
//    s0 - Supplies a pointer to an exception record.
//
//    s1 - Supplies a pointer to a context frame.
//
//    fp - Supplies the same value as sp and is used as a frame pointer.
//
// Return Value:
//
//    None.
//
//--

//
// 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 Fir address of the trap frame. Thus when
// the kernel returns from the trap, the following code is executed directly.
//

        ALTERNATE_ENTRY(KiUserExceptionDispatcher)

        mov     s0, a0                  // set address of exception record
        mov     s1, a1                  // set address of context frame
        bsr     ra, 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.
//

        beq     v0, 10f                 // if eq [false], perform last chance processing

//
// Continue execution.
//

        mov     s1, a0                  // set address of context frame
        ldil    a1, FALSE               // set test alert argument false
        bsr     ra, ZwContinue          // execute system service to continue
        br      zero, 20f               // join common code

//
// Last chance processing.
//

10:     mov     s0, a0                  // set address of exception record
        mov     s1, a1                  // set address of context frame
        ldil    a2, FALSE               // set first chance argument false
        bsr     ra, ZwRaiseException    // perform last chance processing

//
// Common code for unsuccessful completion of the continue or last chance
// service. Use the return status (which is now in v0) as the exception code,
// set noncontinuable exception and attempt to raise another exception. Note
// the stack grows and eventually this loop will end.
//

20:     lda     sp, -ExceptionRecordLength(sp)  // allocate exception record
        mov     sp, a0                          // get address of actual record
        stl     v0, ErExceptionCode(a0)         // set exception code
        ldil    t0, EXCEPTION_NONCONTINUABLE    // set noncontinuable flag
        stl     t0, ErExceptionFlags(a0)        // store exception flags
        stl     s0, ErExceptionRecord(a0)       // set associated exception record
        stl     zero, ErNumberParameters(a0)    // set number of parameters
        bsr     ra, RtlRaiseException           // raise exception
        br      zero, 20b                       // loop on error

        .end    KiUserExceptionDispatch

//++
//
// NTSTATUS
// KiRaiseUserExceptionDispatcher (
//    IN NTSTATUS ExceptionCode
//    )
//
// Routine Description:
//
//    This routine is entered on return from kernel mode to raise a user
//    mode exception.
//
// Arguments:
//
//    v0 - 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 Fir address of the trap frame. Thus when
// the kernel returns from the trap, the following code is executed directly.
//

        .struct 0
RaiseRa: .space  8                       // saved return address
RaiseV0: .space  8                       // saved S0
RaiseExr: .space ExceptionRecordLength  // exception record for RtlRaiseException
RaiseFrameLength:                        // length of handler frame

        NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero)
        lda     sp, -RaiseFrameLength(sp)   // allocate stack frame
        stq     ra, RaiseRa(sp)             // save return address
        PROLOGUE_END

        stq     v0, RaiseV0(sp)                     // save function return status
        stl     v0, ErExceptionCode+RaiseExr(sp)     // set exception code
        stl     zero, ErExceptionFlags+RaiseExr(sp)  // set exception flags
        stl     zero, ErExceptionRecord+RaiseExr(sp) // set exception record
        stl     ra, ErExceptionAddress+RaiseExr(sp)  // set exception address
        stl     zero, ErNumberParameters+RaiseExr(sp)

        lda     a0, RaiseExr(sp)    // set argument to RtlRaiseException
        bsr     ra, RtlRaiseException          // attempt to raise the exception

        ldq     v0, RaiseV0(sp)                // return status

        ldq     ra, RaiseRa(sp)             // restore ra
        lda     sp, RaiseFrameLength(sp)    // deallocate stack frame
        ret     zero, (ra)                  // return

        .end    KiRaiseUserExceptionDispatch