#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
        jalr     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
        jalr     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    FwExceptionHandler
/*++
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)
