summaryrefslogblamecommitdiffstats
path: root/private/ntos/fw/mips/fwtrap.s
blob: cb73aaf0f48fe04fc3d51cb05076cb03e3ff2ca2 (plain) (tree)
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














































































































































































































































































































































































































































































































































































































































































                                                                                                
#if defined(JAZZ) && defined(R4000)
/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

        fw4trap.s

Abstract:

        This module contains the fw exception handling functions.

Author:

        Lluis Abello (lluis)  4-Sep-91

--*/

//
// include header file
//
#include <ksmips.h>
#include <selfmap.h>


.data
//
// Declare the table where to store the processor registers.
// also align it so that we can store doubles.
//
.align 4
ALTERNATE_ENTRY(RegisterTable)
    .space  RegisterTableSize
ALTERNATE_ENTRY(EndRegisterTable)
.text
.set noat
.set noreorder


        SBTTL("User TLB dispatcher")
/*++

Routine Description:

        This routine gets control on a User TLB Miss exception.
        It reads the TLB Miss handler address from the System
        Parameter Block and jumps to it.

Arguments:

    None.

Return Value:

    None.

--*/
        LEAF_ENTRY(FwTbMissStartAddress)
        li      k0,KSEG0_BASE
        lw      k0,0x1018(k0)// Load address of UTLB handler
        nop
        jal     k1,k0                       // jump to handler saving return
        nop                                 // address in k1
        mtc0    k0,epc                      // Handler returns return address in K0
        nop                                 // 2 cycle hazard
        nop                                 //
        eret                                // restore from exception
        nop
        ALTERNATE_ENTRY(FwTbMissEndAddress)
        .end  FwTbMissStartAddress


        SBTTL("General Exception dispatcher")
/*++

Routine Description:

        This routine gets control on a General exception.
        It reads the General exception handler address from the System
        Parameter Block and jumps to it.

Arguments:

    None.

Return Value:

    None.

--*/
        LEAF_ENTRY(FwGeneralExceptionStartAddress)

        li      k0,KSEG0_BASE
        lw      k0,0x1014(k0)// Load address of GE handler
        nop
        jal     k1,k0                       // jump to handler saving return
        nop                                 // address in k1
        mtc0    k0,epc                      // Handler returns return address in K0
        nop                                 // 2 cycle hazard
        nop                                 //
        eret                                // restore from exception
        nop
        ALTERNATE_ENTRY(FwGeneralExceptionEndAddress)

        .end    FwGeneralExceptionStartAddress


        SBTTL("CacheError Exception dispatcher")
/*++

Routine Description:

        This routine gets control on a cache error exception.
        It jumps to the Monitor exception handler.
        It gets copied to address 0xA0000100

Arguments:

    None.

Return Value:

    None.

--*/

        LEAF_ENTRY(FwCacheErrorExceptionStartAddress)

        la      k0,CacheExceptionMonitorEntry   // load address of Monitor entry
        li      k1,KSEG1_BASE                   // convert address to non cached
        or      k0,k0,k1                        //
        j       k0
        nop

        ALTERNATE_ENTRY(FwCacheErrorExceptionEndAddress)
        .end    FwCacheErrorExceptionStartAddress

        SBTTL("Firmware Exception intialize")
/*++

Routine Description:

        This routine copies the User TLB dispatcher and
        the General Exception dispatcher to the R4000 vector
        addresses.

Arguments:

    None.

Return Value:

    None.

--*/
//
// Copy the TB miss and general exception dispatchers to low memory.
//
        NESTED_ENTRY(FwExceptionInitialize,4, zero)
        subu    sp,sp,4                 // decrement stack pointer
        sw      ra,0(sp)                // save ra
        la      t2,FwTbMissStartAddress // get user TB miss start address
        la      t3,FwTbMissEndAddress   // get user TB miss end address
        li      t4,KSEG1_BASE           // get copy address
10:     lw      t5,0(t2)                // copy code to low memory
        addu    t2,t2,4                 // advance copy pointers
        sw      t5,0(t4)                //
        bne     t2,t3,10b               // if ne, more to copy
        addu    t4,t4,4                 //

        la      t2,FwGeneralExceptionStartAddress   // get general exception start address
        la      t3,FwGeneralExceptionEndAddress     // get general exception end address
        li      t4,KSEG1_BASE + 0x180   // get copy address
20:     lw      t5,0(t2)                // copy code to low memory
        addu    t2,t2,4                 // advance copy pointers
        sw      t5,0(t4)                //
        bne     t2,t3,20b               // if ne, more to copy
        addu    t4,t4,4                 //

        la      t2,FwCacheErrorExceptionStartAddress // get cache error exception start address
        la      t3,FwCacheErrorExceptionEndAddress   // get cache error exception end address
        li      t4,KSEG1_BASE + 0x100   // get copy address
30:     lw      t5,0(t2)                // copy code to low memory
        addu    t2,t2,4                 // advance copy pointers
        sw      t5,0(t4)                //
        bne     t2,t3,30b               // if ne, more to copy
        addu    t4,t4,4                 //

        //
        // Set the Monitor exception handler address in the vector.
        //
        li      t0,KSEG0_BASE
        la      t1,FwExceptionHandler
        sw      t1,0x1014(t0)    // Init GE vector
        sw      t1,0x1018(t0)    // Init UTLB vector

        jal     HalSweepIcache          // sweep the instruction cache
        nop
        jal     HalSweepDcache          // sweep the data cache
        nop

        //
        // Clear the BEV in the psr and enable IO device interrupts
        //
        li      t0,(1<<PSR_CU1) | (1 << PSR_IE) | (1<< (PSR_INTMASK+3))
        mtc0    t0,psr                  // set new psr.
        nop                             //
        nop                             //
        nop                             //
        lw      ra,0(sp)                // load return address
        addiu   sp,sp,4                 // restore stack pointer
        j       ra                      // return to caller
        nop
        .end    FwExceptionInitialize


        SBTTL("Monitor Exception Handler")
/*++

Routine Description:

        This routine implements the exception handler for
        the monitor.

Arguments:

        k1 specifies the caller source in the following way:

         -  If k1 contains a word aligned address. The exception is
            either a UTLB or a GE. The cause register supplies the cause
            of the exception.

         -  Otherwise k1 contains a value that identifies the caller.

    None.

Return Value:

    None.

--*/

        LEAF_ENTRY(FwExceptionHandler)
        mfc0    k0,cause                // read cause register
        nop
        andi    k0,k0,R4000_XCODE_MASK  // isolate cause of exception
        bne     k0,zero,10f             // if not interrupt go to Monitor
        nop
        subu    sp,sp,FwFrameSize       // decrement state pointer
        sw      k1,FwFrameK1(sp)        // save volatile state
        sw      ra,FwFrameRa(sp)        // save volatile state
        sw      a0,FwFrameA0(sp)        // save volatile state
        sw      a1,FwFrameA1(sp)        // save volatile state
        sw      a2,FwFrameA2(sp)        // save volatile state
        sw      a3,FwFrameA3(sp)        // save volatile state
        sw      v0,FwFrameV0(sp)        // save volatile state
        sw      v1,FwFrameV1(sp)        // save volatile state
        sw      t0,FwFrameT0(sp)        // save volatile state
        sw      t1,FwFrameT1(sp)        // save volatile state
        sw      t2,FwFrameT2(sp)        // save volatile state
        sw      t3,FwFrameT3(sp)        // save volatile state
        sw      t4,FwFrameT4(sp)        // save volatile state
        sw      t5,FwFrameT5(sp)        // save volatile state
        sw      t6,FwFrameT6(sp)        // save volatile state
        sw      t7,FwFrameT7(sp)        // save volatile state
        sw      t8,FwFrameT8(sp)        // save volatile state
        sw      t9,FwFrameT9(sp)        // save volatile state
        mfc0    a0,cause                // read cause register
        mfc0    a1,psr                  // read psr.
        sw      AT,FwFrameAT(sp)        // save volatile state
        jal     FwInterruptHandler      // call c routine
        and     a0,a0,a1                // mask IM to clear disabled interrupts.

        move    k0,v0                   // save return value in k0
        lw      k1,FwFrameK1(sp)        // restore volatile state
        lw      ra,FwFrameRa(sp)        // restore volatile state
        lw      a0,FwFrameA0(sp)        // restore volatile state
        lw      a1,FwFrameA1(sp)        // restore volatile state
        lw      a2,FwFrameA2(sp)        // restore volatile state
        lw      a3,FwFrameA3(sp)        // restore volatile state
        lw      v0,FwFrameV0(sp)        // restore volatile state
        lw      v1,FwFrameV1(sp)        // restore volatile state
        lw      t0,FwFrameT0(sp)        // restore volatile state
        lw      t1,FwFrameT1(sp)        // restore volatile state
        lw      t2,FwFrameT2(sp)        // restore volatile state
        lw      t3,FwFrameT3(sp)        // restore volatile state
        lw      t4,FwFrameT4(sp)        // restore volatile state
        lw      t5,FwFrameT5(sp)        // restore volatile state
        lw      t6,FwFrameT6(sp)        // restore volatile state
        lw      t7,FwFrameT7(sp)        // restore volatile state
        lw      t8,FwFrameT8(sp)        // restore volatile state
        lw      t9,FwFrameT9(sp)        // restore volatile state
        lw      AT,FwFrameAT(sp)        // save volatile state
        beq     k0,zero,10f             // if routine returned false do not return
        addiu   sp,sp,FwFrameSize       // restore stack pointer
        mfc0    k0,epc                  // get return address
        nop
        j       k1                      // return to caller
        nop                             //
//
//  Control reaches here if an exception other than an interrupt occurred
//  or if the interrupt handler returned false meaning that it didn't
//  know how to handle the interrupt exception.
//
//  At this time no state has been destroyed and register k1 still contains
//  the address where to return.
//
10:
        addiu   k0,k0,-XCODE_BREAKPOINT // substract breakpoint code
        beq     k0,zero,10f             // if eq Bp exception, call KD unconditionally
        la      k0,KdInstalled          // load address of boolean
        lbu     k0,0(k0)                // read boolean
        beq     k0,zero,MonitorExceptionHandler // if false go to the monitor
        nop                             // otherwise
10:     j       KiGeneralException      // go to the debugger.
        nop
    //
    // This is the entry point when the monitor is called.
    // Set k1 to 3 to tell the Monitor than can execute a j ra
    // to quit.
    //
    ALTERNATE_ENTRY(FwMonitor)
        b       MonitorExceptionHandler
        li      k1,3                    // set k1 to 3 to indicate we don't get
        nop                             // here because of an exception
                                        // The monitor returns to ra which points
                                        // to where we got called from.

    ALTERNATE_ENTRY(CacheExceptionMonitorEntry)
        la      k0,RegisterTable        // get address of table.
        or      k0,k0,k1                // set it non cached.
        b       MonitorEntry            // go to monitor
        ori     k1,zero,CACHE_EXCEPTION // set flag to tell that parity error occurred

    ALTERNATE_ENTRY(MonitorExceptionHandler)
        //
        // Save the registers.
        //
        la      k0,RegisterTable        // get address of table.

    ALTERNATE_ENTRY(MonitorEntry)

        sw      zero,zeroRegTable(k0)
        sw      AT,atRegTable(k0)
        sw      v0,v0RegTable(k0)
        sw      v1,v1RegTable(k0)
        sw      a0,a0RegTable(k0)
        sw      a1,a1RegTable(k0)
        sw      a2,a2RegTable(k0)
        sw      a3,a3RegTable(k0)
        sw      t0,t0RegTable(k0)
        sw      t1,t1RegTable(k0)
        sw      t2,t2RegTable(k0)
        sw      t3,t3RegTable(k0)
        sw      t4,t4RegTable(k0)
        sw      t5,t5RegTable(k0)
        sw      t6,t6RegTable(k0)
        sw      t7,t7RegTable(k0)
        sw      s0,s0RegTable(k0)
        sw      s1,s1RegTable(k0)
        sw      s2,s2RegTable(k0)
        sw      s3,s3RegTable(k0)
        sw      s4,s4RegTable(k0)
        sw      s5,s5RegTable(k0)
        sw      s6,s6RegTable(k0)
        sw      s7,s7RegTable(k0)
        sw      t8,t8RegTable(k0)
        sw      t9,t9RegTable(k0)
        sw      k0,k0RegTable(k0)
        sw      k1,k1RegTable(k0)
        sw      gp,gpRegTable(k0)
        sw      sp,spRegTable(k0)
        sw      s8,s8RegTable(k0)
        sw      ra,raRegTable(k0)

        //
        //  Store coprocessor1 registers.
        //
        cfc1    v0,fsr
        sdc1    f0,f0RegTable(k0)
        sdc1    f2,f2RegTable(k0)
        sdc1    f4,f4RegTable(k0)
        sdc1    f6,f6RegTable(k0)
        sdc1    f8,f8RegTable(k0)
        sdc1    f10,f10RegTable(k0)
        sdc1    f12,f12RegTable(k0)
        sdc1    f14,f14RegTable(k0)
        sdc1    f16,f16RegTable(k0)
        sdc1    f18,f18RegTable(k0)
        sdc1    f20,f20RegTable(k0)
        sdc1    f22,f22RegTable(k0)
        sdc1    f24,f24RegTable(k0)
        sdc1    f26,f26RegTable(k0)
        sdc1    f28,f28RegTable(k0)
        sdc1    f30,f30RegTable(k0)
        sw      v0,fsrRegTable(k0)
        //
        //  Store cop0 registers.
        //
        mfc0    v1,index
        mfc0    v0,random
        sw      v1,indexRegTable(k0)
        sw      v0,randomRegTable(k0)
        mfc0    v1,entrylo0
        nop
        mfc0    v0,entrylo1
        sw      v1,entrylo0RegTable(k0)
        sw      v0,entrylo1RegTable(k0)
        mfc0    v1,context
        mfc0    v0,pagemask
        sw      v1,contextRegTable(k0)
        sw      v0,pagemaskRegTable(k0)
        mfc0    v1,wired
        mfc0    v0,badvaddr
        sw      v1,wiredRegTable(k0)
        sw      v0,badvaddrRegTable(k0)
        mfc0    v1,count
        mfc0    v0,entryhi
        sw      v1,countRegTable(k0)
        sw      v0,entryhiRegTable(k0)
        mfc0    v1,compare
        mfc0    v0,psr
        sw      v1,compareRegTable(k0)
        sw      v0,psrRegTable(k0)
        mfc0    v1,cause
        mfc0    v0,epc
        sw      v1,causeRegTable(k0)
        sw      v0,epcRegTable(k0)
        mfc0    v1,prid
        mfc0    v0,config
        sw      v1,pridRegTable(k0)
        sw      v0,configRegTable(k0)
        mfc0    v1,lladdr
        mfc0    v0,watchlo
        sw      v1,lladdrRegTable(k0)
        sw      v0,watchloRegTable(k0)
        mfc0    v1,watchhi
        mfc0    v0,ecc
        sw      v1,watchhiRegTable(k0)
        sw      v0,eccRegTable(k0)
        mfc0    v1,cacheerr
        mfc0    v0,taglo
        sw      v1,cacheerrorRegTable(k0)
        sw      v0,tagloRegTable(k0)
        mfc0    v1,taghi
        mfc0    v0,errorepc
        sw      v1,taghiRegTable(k0)
        sw      v0,errorepcRegTable(k0)
        li      k0,CACHE_EXCEPTION
        bne     k0,k1,10f                       // check if parity exception
        la      k0,Monitor                      // Set address where to return
        li      t0,KSEG1_BASE                   // convert it to non cached
        or      k0,k0,t0                        //
        mtc0    k0,errorepc                     // set in errorepc
        li      sp, RAM_TEST_STACK_ADDRESS | KSEG1_BASE
        andi    a0,k1,0x3                       // pass caller source in a0 just 2 bits.
        eret                                    // jump to the monitor and leave exception level
10:
        la      k0,Monitor                      // Set address where to
        mtc0    k0,epc                          // return from the exception.
        li      k0,0x3
        beq     k0,k1,10f                       // if k1=3 do not change sp
        andi    a0,k1,0x3                       // pass caller source in a0 just 2 bits.
        li      sp,RAM_TEST_STACK_ADDRESS       //  set the stack pointer.
        eret                                    // jump to the monitor and leave exception level
10:     j       Monitor

        .end    MonitorExceptionHandler
/*++
ARC_STATUS
FwInvoke(
    IN ULONG ExecAddr,
    IN ULONG StackAddr,
    IN ULONG Argc,
    IN PCHAR Argv[],
    IN PCHAR Envp[]
    )


Routine Description:

    This routine invokes a loaded program.

Arguments:

    ExecAddr - Supplies the address of the routine to call.

    StackAddr - Supplies the address to which the stack pointer is set.

    Argc, Argv, Envp - Supply the arguments and endvironment to pass to
                       Loaded program.

    The stack pointer is saved in register s0 so that when the loaded
    program returns, the old stack pointer can be restored.

    In addition, the value stored in InvokeSavedSp is read and if zero,
    this is the first loaded program, thus the current stack pointer
    is the firmware stack pointer and it is saved in order to
    be able to restore the Firmware stack pointer when a loaded program
    calls ReturnFromMain.

Return Value:

    ESUCCESS is returned if the address is valid.
    EFAULT indicates an invalid addres.

--*/

{
    NESTED_ENTRY(FwInvoke,0x20,zero)

        subu    sp,sp,0x20                  // allocate room in the stack.
        sw      ra,0x1C(sp)                 // save ra
        sw      s0,0x18(sp)                 // save s0
        move    v1,a0                       // save address of routine to call
        andi    v0,v1,0x3                   // check for alignment.
        bne     v0,zero,20f                 // if not aligned return error.
        li      v0,6                        // set EFAULT = 6 a return value.
        move    a0,a2                       // move argc to first argument
        move    s0,sp                       // save stack pointer in s0
        lw      a2,0x30(sp)                 // load Envp argument into second.
        move    sp,a1                       // set new stack pointer.
        subu    sp,sp,0x10                  // make room for arguments.
        jal     v1                          // call program
        move    a1,a3                       // move argv to right argument
//
// If loaded program returns, s0 has the previous stack pointer.
//
        move    sp,s0                       // set old stack pointer.
        move    v0,zero                     // set ESUCCESS as return value.
        lw      ra,0x1C(sp)                 // restore ra
        lw      s0,0x18(sp)                 // restore s0
20:
        j       ra                          // return to caller
        addiu   sp,sp,0x20                  // restore sp.
    .end FwInvoke


        SBTTL("FwExecute")
/*++

Routine Description:

    This routine is the entry point for the Execute service.

    It behaves in two different ways depending on where it is
    called from:

    1) If it's called from the Firmware it saves the stack pointer
    in a fixed location and then saves all the saved registers
    in the stack. This is the stack that will be used to restore
    the saved state when returning to the firmware.

    2) If called from a loaded program the program to be loaded
    and executed can overwrite the current program and it's
    stack, therefore a temporary stack is set.

Arguments:

    a0 IN PCHAR Path,
    a1 IN ULONG Argc,
    a2 IN PCHAR Argv[],
    a3 IN PCHAR Envp[]

Return Value:

    ARC_STATUS returned by FwPrivateExecute.
    Always returns to the Firmware.

--*/
    LEAF_ENTRY(FwExecute)
        la      t0, FwSavedSp               // load address where to save Fw SP
        lw      t1, 0(t0)                   // read saved stack
        beq     t1, zero,CallFromFw         // if zero first call from Fw.
        nop
//
//  A loaded program wants to be replaced by another program
//  therefore the current stack and state will be trashed and a
//  new temporary stack needs to be set.
//
        la      t0, FwTemporaryStack        // load address of Fw temporary stack
        lw      sp, 0(t0)                   // load Tmp Stack value.
        jal     FwPrivateExecute            // go to do the job
        nop
//
//      if the program returns it's caller is gone, therefore
//      we restore the initiali fw stack and returned to the
//      firmware instead.
//
        la      t0, FwSavedSp               // get address of saved stack
        lw      sp, 0(t0)                   // restore saved stack.
        b       RestoreFwState              // go to restore state
        nop

CallFromFw:
        subu    sp,sp,0x30                  // make room in the stack
        sw      sp, 0(t0)                   // save new stack
        sw      ra, 0x4(sp)                 // save also return address
        sw      s0, 0x8(sp)                 // save state that must be preserved.
        sw      s1, 0xC(sp)
        sw      s2, 0x10(sp)
        sw      s3, 0x14(sp)
        sw      s4, 0x18(sp)
        sw      s5, 0x1C(sp)
        sw      s6, 0x20(sp)
        sw      s7, 0x24(sp)
        sw      s8, 0x28(sp)
        jal     FwPrivateExecute            // go to do the job
        sw      gp, 0x2C(sp)                // save global pointer

RestoreFwState:
        lw      ra, 0x4(sp)                 // restore ra
        lw      s0, 0x8(sp)                 // restore Fw state
        lw      s1, 0xC(sp)
        lw      s2, 0x10(sp)
        lw      s3, 0x14(sp)
        lw      s4, 0x18(sp)
        lw      s5, 0x1C(sp)
        lw      s6, 0x20(sp)
        lw      s7, 0x24(sp)
        lw      s8, 0x28(sp)
        lw      gp, 0x2C(sp)
        j       ra                          // return to Firmware.
        addu    sp,sp,0x30                  // restore sp.

    .end    FwExecute


        SBTTL("Termination Functions")
/*++

Routine Description:

    This routine implements the Firmware ReturnFromMain
    termination function.

Arguments:

    None.

Return Value:

    returns to where the program was called from.

--*/
    ALTERNATE_ENTRY(FwReturnFromMain)
    la      t0,FwSavedSp                // get address where SP was saved
    lw      sp,0(t0)                    // set old stack pointer.
    b       RestoreFwState
    move    v0,zero                     // set ESUCCESS as return value.

    .end    FwReturnFromMain

#endif  // defined(JAZZ) && defined(R4000)