/* excArchLib.c - PowerPC exception handling facilities */ /* Copyright 1984-1998 Wind River Systems, Inc. */ #include "copyright_wrs.h" /* modification history -------------------- 01z,02aug00,crg added optional extended (32-bit addressing) exception stubs for non-PPC403 processors 01y,24aug98,cjtc intEnt logging for PPC is now performed in a single step instead of the two-stage approach which gave problems with out-of-order timestamps in the event log. Global evtTimeStamp no longer required (SPR 21868) 01x,18aug98,tpr added PowerPC EC 603 support. 01w,09jan98,dbt modified for new breakpoint scheme 01v,06aug97,tam fixed problem with CE interrupt (SPR #8964) 01u,26mar97,tam added test for DBG_BREAK_INST in excExcHandle() (SPR #8217). 01t,26mar97,jdi,tam doc cleanup. 01s,20mar97,tam added function excIntCrtConnect for PPC403 Critical Intr. 01r,24feb97,tam added support for 403GC/GCX exceptions. 01q,10feb97,tam added support to handle floating point exceptions (SPR #7840). 01p,05feb97,tpr reawork PPC860 support in excExcHandle() (SPR 7881). 01o,16jan97,tpr Changed CACHE_TEXT_UPDATE() address in excVecSet(). (SPR #7754) 01n,03oct96,tpr Reworked excGetInfoFromESF () to include DSISR and DAR registers for PPC860 (SPR# 7254) 01o,11jul96,pr cleanup windview instrumentation 01n,08jul96,pr added windview instrumentation - conditionally compiled 01m,31may96,tpr added PowerPC 860 support. 01l,12mar96,tam re-worked exception handling for the PPC403 FIT and PIT interrupts. Added excIntConnectTimer(). 01k,28feb96,tam added excCrtConnect() for critical exceptions on the PPC403 cpu. 01j,27feb96,ms made excConnectCode use "stwu" instead of "addi". fixed compiler warnings. Change logMsg to func_logMsg. 01i,23feb96,tpr added excConnect() & excIntConnect(), renamed excCode[] by excConnectCode. 01h,05oct95,tpr changed excCode[] code. 01g,23may95,caf fixed unterminated comment in version 01f. 01f,22may95,caf enable PowerPC 603 MMU. 01e,09feb95,yao changed machine check exception handler to excCrtStub for PPC403. 01d,07feb95,yao fixed excExcHandler () for PPC403. removed _AIX_TOOL support. 01c,02feb95,yao changed to set timer exceptions to excClkStub for PPC403. changed to call vxEvpr{S,G}et for PPC403. 01b,07nov94,yao cleanup. 01a,09sep94,yao written. */ /* This module provides architecture-dependent facilities for handling PowerPC exceptions. See excLib for the portions that are architecture independent. INITIALIZATION Initialization of exception handling facilities is in two parts. First, excVecInit() is called to set all the PowerPC exception, trap, and interrupt vectors to the default handlers provided by this module. The rest of this package is initialized by calling excInit() (in excLib), which spawns the exception support task, excTask(), and creates the pipe used to communicate with it. See the manual entry for excLib for more information. SEE ALSO: excLib, .pG "Debugging" */ /* LINTLIBRARY */ #include "vxWorks.h" #include "esf.h" #include "iv.h" #include "sysLib.h" #include "intLib.h" #include "msgQLib.h" #include "signal.h" #include "cacheLib.h" #include "errnoLib.h" #include "string.h" #include "rebootLib.h" #include "excLib.h" #include "vxLib.h" #include "private/funcBindP.h" #include "private/sigLibP.h" #include "private/taskLibP.h" #include "wdb/wdbDbgLib.h" typedef struct excTbl { UINT32 vecOff; /* vector offset */ STATUS (*excCnctRtn) (); /* routine to connect the exception handler*/ void (*excHandler) (); /* exception handler routine */ } EXC_TBL; /* externals */ IMPORT FUNCPTR excExcepHook; /* add'l rtn to call when exceptions occur */ IMPORT void excEnt (void); /* exception entry routine */ IMPORT void excExit (void); /* exception exit routine */ #if (CPU == PPC403) IMPORT void excCrtExit (void); /* critical exception stub */ IMPORT void excCrtEnt (void); /* critical exception stub */ IMPORT void intCrtExit (void); /* external critical exception stub */ IMPORT void intCrtEnt (void); /* external critical exception stub */ IMPORT FUNCPTR _dbgDsmInstRtn; #endif /* (CPU == PPC403) */ IMPORT void intEnt (void); /* interrupt entry routine */ IMPORT void intExit (void); /* interrupt exit routine */ IMPORT void excEPSet (FUNCPTR *); /* globals */ FUNCPTR _func_excTrapRtn = NULL; /* trap handling routine */ #if (CPU != PPC403) BOOL excExtendedStubs = FALSE; /* extended exception stubs flag */ #endif /* locals */ LOCAL FUNCPTR * excVecBase = 0; /* exception vector base address */ LOCAL int excGetInfoFromESF (FAST int vecNum, FAST ESFPPC *pEsf, EXC_INFO *pExcInfo); /* forward declarations */ void excExcHandle (ESFPPC * pEsf); void excIntHandle (); void excVecSet (FUNCPTR * vector, FUNCPTR function); FUNCPTR * excVecBaseGet (void); STATUS excConnect (VOIDFUNCPTR *, VOIDFUNCPTR); LOCAL EXC_TBL excBlTbl[] = { #if (CPU == PPC403) {_EXC_OFF_CRTL, excIntCrtConnect, excIntHandle},/* critical intr. */ {_EXC_OFF_MACH, excCrtConnect, excExcHandle}, /* machine check */ {_EXC_OFF_PROT, excConnect, excExcHandle}, /* protection vlt */ {_EXC_OFF_INST, excConnect, excExcHandle}, /* instr. access */ {_EXC_OFF_INTR, excIntConnect, excIntHandle}, /* external intr */ {_EXC_OFF_ALIGN, excConnect, excExcHandle}, /* alignment */ {_EXC_OFF_PROG, excConnect, excExcHandle}, /* program */ {_EXC_OFF_SYSCALL, excConnect, excExcHandle}, /* system call */ {_EXC_OFF_PIT, excIntConnectTimer, excIntHandle}, /* prog timer */ {_EXC_OFF_FIT, excIntConnectTimer, excIntHandle},/* fixed timer */ {_EXC_OFF_WD, excIntCrtConnect, excIntHandle},/* watchdog timer */ {_EXC_OFF_DATA_MISS, excConnect, excExcHandle}, /* data TLB miss */ {_EXC_OFF_INST_MISS, excConnect, excExcHandle}, /* inst. TLB miss */ {_EXC_OFF_DBG, excCrtConnect, excExcHandle}, /* debug events */ #elif (CPU == PPC601) {_EXC_OFF_RESET, excConnect, excExcHandle}, /* system reset */ {_EXC_OFF_MACH, excConnect, excExcHandle}, /* machine check */ {_EXC_OFF_DATA, excConnect, excExcHandle}, /* data access */ {_EXC_OFF_INST, excConnect, excExcHandle}, /* instr. access */ {_EXC_OFF_INTR, excIntConnect, excIntHandle}, /* external intr */ {_EXC_OFF_ALIGN, excConnect, excExcHandle}, /* alignment */ {_EXC_OFF_PROG, excConnect, excExcHandle}, /* program */ {_EXC_OFF_FPU, excConnect, excExcHandle}, /* fp unavailable */ {_EXC_OFF_DECR, excIntConnect, excIntHandle}, /* decrementer */ {_EXC_OFF_IOERR, excConnect, excExcHandle}, /* i/o ctrl error */ {_EXC_OFF_SYSCALL, excConnect, excExcHandle}, /* system call */ {_EXC_OFF_RUN_TRACE, excConnect, excExcHandle}, /* run mode/trace */ #elif ((CPU == PPC603) || (CPU == PPCEC603) || (CPU == PPC604)) {_EXC_OFF_RESET, excConnect, excExcHandle}, /* system reset */ {_EXC_OFF_MACH, excConnect, excExcHandle}, /* machine check */ {_EXC_OFF_DATA, excConnect, excExcHandle}, /* data access */ {_EXC_OFF_INST, excConnect, excExcHandle}, /* instr. access */ {_EXC_OFF_INTR, excIntConnect, excIntHandle}, /* external intr. */ {_EXC_OFF_ALIGN, excConnect, excExcHandle}, /* alignment */ {_EXC_OFF_PROG, excConnect, excExcHandle}, /* program */ {_EXC_OFF_FPU, excConnect, excExcHandle}, /* fp unavailable */ {_EXC_OFF_DECR, excIntConnect, excIntHandle}, /* decrementer */ {_EXC_OFF_SYSCALL, excConnect, excExcHandle}, /* system call */ {_EXC_OFF_TRACE, excConnect, excExcHandle}, /* trace exception*/ #if ((CPU == PPC603) || (CPU == PPCEC603)) {_EXC_OFF_INST_MISS, excConnect, excExcHandle}, /* inst trsl miss */ {_EXC_OFF_LOAD_MISS, excConnect, excExcHandle}, /* data trsl miss */ {_EXC_OFF_STORE_MISS, excConnect, excExcHandle}, /* data trsl miss */ #else /* (CPU == PPC604) */ {_EXC_OFF_PERF, excConnect, excExcHandle}, /* perf. monitor */ #endif /* (CPU == PPC604) */ {_EXC_OFF_INST_BRK, excConnect, excExcHandle}, /* instr. addr BP */ {_EXC_OFF_SYS_MNG, excConnect, excExcHandle}, /* system managmnt*/ #elif (CPU == PPC860) {_EXC_OFF_RESET, excConnect, excExcHandle}, /* system reset */ {_EXC_OFF_MACH, excConnect, excExcHandle}, /* machine check */ {_EXC_OFF_DATA, excConnect, excExcHandle}, /* data access */ {_EXC_OFF_INST, excConnect, excExcHandle}, /* instr. access */ {_EXC_OFF_INTR, excIntConnect, excIntHandle}, /* external intr. */ {_EXC_OFF_ALIGN, excConnect, excExcHandle}, /* alignment */ {_EXC_OFF_PROG, excConnect, excExcHandle}, /* program */ {_EXC_OFF_FPU, excConnect, excExcHandle}, /* fp unavailable */ {_EXC_OFF_DECR, excIntConnect, excIntHandle}, /* decrementer */ {_EXC_OFF_SYSCALL, excConnect, excExcHandle}, /* system call */ {_EXC_OFF_TRACE, excConnect, excExcHandle}, /* trace exception*/ {_EXC_OFF_SW_EMUL, excConnect, excExcHandle}, /* Software Emul. */ {_EXC_OFF_INST_MISS, excConnect, excExcHandle}, /* Instr TLB Miss */ {_EXC_OFF_DATA_MISS, excConnect, excExcHandle}, /* Data TLB Miss */ {_EXC_OFF_INST_ERROR, excConnect, excExcHandle}, /* Instr TLB Error*/ {_EXC_OFF_DATA_ERROR, excConnect, excExcHandle}, /* Data TLB Error */ {_EXC_OFF_DATA_BKPT, excConnect, excExcHandle}, /* Data Breakpoint*/ {_EXC_OFF_INST_BKPT, excConnect, excExcHandle}, /* Instr Breakpt */ {_EXC_OFF_PERI_BKPT, excConnect, excExcHandle}, /* Peripheral BP */ {_EXC_OFF_NM_DEV_PORT, excConnect, excExcHandle}, /* Non Maskable */ #endif /* (CPU == PPC604) */ {0, (STATUS (*)()) NULL, (void (*) ()) NULL}, }; /* * it is necessary to clear the MSR[CE] bit upon an external interrupt * on the PowerPC 403 architecture, to prevent this interrupt being * interrupted by a critical interrupt before the context is saved. * There's still a window of 5 instructions were the external interrupt * can be interrupted. This is taken care of, in the critical interrupt * entry code (intCrtEnt) by saving SPRG3 before using it and restoring * its original value later on. */ LOCAL INSTR excConnectCode[]= { 0x7c7343a6, /* 0x00 mtspr SPRG3, p0 */ #ifdef _EXC_OFF_CRTL 0x7c6000a6, /* 0x04 mfmsr p0 */ 0x546303da, /* 0x08 rlwinm p0,p0,0,15,13 clear MSR [CE] */ 0x7c600124, /* 0x0c mtmsr p0 */ 0x4c00012c, /* 0x10 isync */ #endif /* _EXC_OFF_CRTL */ 0x7c6802a6, /* 0x14 mflr p0 */ 0x48000003, /* 0x18 bla xxxEnt */ 0x38610000, /* 0x1c addi r3, sp, 0 */ 0x9421fff0, /* 0x20 stwu sp, -FRAMEBASESZ(sp) */ 0x48000003, /* 0x24 bla xxxHandler */ 0x38210010, /* 0x28 addi sp, sp, FRAMEBASESZ */ 0x48000003 /* 0x2c bla xxxExit */ }; #if (CPU != PPC403) LOCAL INSTR excExtConnectCode[]= { 0x7c7343a6, /* 0x00 mtspr SPRG3, p0 */ 0x7c6802a6, /* 0x04 mflr p0 */ 0x7c7043a6, /* 0x08 mtspr SPRG0, p0 */ 0x3c600000, /* 0x0c lis p0, HI(xxxEnt) */ 0x60630000, /* 0x10 ori p0, p0, LO(xxxEnt) */ 0x7c6803a6, /* 0x14 mtlr p0 */ 0x7c7042a6, /* 0x18 mfspr p0, SPRG0 */ 0x4e800021, /* 0x1c blrl */ 0x3c600000, /* 0x20 lis p0, HI(xxxHandler) */ 0x60630000, /* 0x24 ori p0, p0, LO(xxxHandler) */ 0x7c6803a6, /* 0x28 mtlr p0 */ 0x38610000, /* 0x2c addi p0, sp, 0 */ 0x9421fff0, /* 0x30 stwu sp, -FRAMEBASESZ(sp) */ 0x4e800021, /* 0x34 blrl */ 0x38210010, /* 0x38 addi sp, sp, FRAMEBASESZ */ 0x3c600000, /* 0x3c lis p0, HI(xxxExit) */ 0x60630000, /* 0x40 ori p0, p0, LO(xxxExit) */ 0x7c6803a6, /* 0x44 mtlr p0 */ 0x4e800021 /* 0x48 blrl */ }; #endif #ifdef _EXC_OFF_CRTL LOCAL INSTR excCrtConnectCode[]= { 0x7c7243a6, /* 0x00 mtspr SPRG2, p0 */ 0x7c6802a6, /* 0x14 mflr p0 */ 0x48000003, /* 0x18 bla xxxEnt */ 0x38610000, /* 0x1c addi r3, sp, 0 */ 0x9421fff0, /* 0x20 stwu sp, -FRAMEBASESZ(sp) */ 0x48000003, /* 0x24 bla xxxHandler */ 0x38210010, /* 0x28 addi sp, sp, FRAMEBASESZ */ 0x48000003 /* 0x2c bla xxxExit */ }; /* offsets in the excConnectCode and excCrtConnectCode arrays */ # define ENT_OFF 6 /* offset for intEnt/excEnt */ # define ISR_OFF 9 /* offset for ISR or exc. handler */ # define EXIT_OFF 11 /* offset for intExit/excExit */ # define ENT_CRT_OFF 2 /* offset for intEnt/excEnt */ # define ISR_CRT_OFF 5 /* offset for ISR or exc. handler */ # define EXIT_CRT_OFF 7 /* offset for intCrtExit/excCrtExit */ #else /* _EXC_OFF_CRTL */ # define ENT_OFF 2 /* offset for intEnt/excEnt */ # define ISR_OFF 5 /* offset for ISR or exc. handler */ # define EXIT_OFF 7 /* offset for intExit/excExit */ # define EXT_ENT_OFF 3 /* offset for ext intEnt/excEnt */ # define EXT_ISR_OFF 8 /* offset for ext ISR or exc. handler */ # define EXT_EXIT_OFF 15 /* offset for ext intExit/excExit */ #endif /* _EXC_OFF_CRTL */ #if (CPU == PPC403) /* * the following stub is used with the PIT and FIT timer interrupt * vector. Since there's not enough space to put the above assembly stub * for the FIT or PIT at there normal location (0x1000 & 0x1010), it is * necessary to jump to another location were the stubs are being put * these locations are defined by _EXC_NEW_OFF_PIT and _EXC_NEW_OFF_FIT. */ LOCAL INSTR excConnectCodeTimer[]= { 0x48000002, /* 0x00 ba xxxEnt */ }; #endif /******************************************************************************* * * excVecInit - initialize the exception vectors * * This routine sets up PowerPC exception vectors to point to the * appropriate default exception handlers. * * WHEN TO CALL * This routine is usually called from the system start-up routine * usrInit() in usrConfig.c, before interrupts are enabled. * * RETURNS: OK (always). * * SEE ALSO: excLib */ STATUS excVecInit (void) { FAST int ix; for (ix = 0; excBlTbl[ix].excHandler != (void (*)()) NULL; ix++) { excBlTbl[ix].excCnctRtn ((VOIDFUNCPTR *) excBlTbl[ix].vecOff, (VOIDFUNCPTR ) excBlTbl[ix].excHandler); } return (OK); } /******************************************************************************* * * excConnect - connect a C routine to an exception vector * * This routine connects a specified C routine to a specified exception * vector. An exception stub is created and in placed at in the * exception table. The address of is stored in the exception stub * code. When an exception occurs, the processor jumps to the exception stub * code, saves the registers, and call the C routines. * * The routine can be any normal C code, except that it must not * invoke certain operating system functions that may block or perform * I/O operations. * * The registers are saved to an Exception Stack Frame (ESF) which is placed on the * stack of the task that has produced the exception. The ESF structure * is defined in /h/arch/ppc/esfPpc.h. * * The only argument passed by the exception stub to the C routine is a pointer * to the ESF containing the registers values. The prototype of this C routine * is as follows: * .CS * void excHandler (ESFPPC *); * .CE * * When the C routine returns, the exception stub restores the registers saved * in the ESF and continues execution of the current task. * * RETURNS: OK, always. * * SEE ALSO: excIntConnect(), excVecSet() * */ STATUS excConnect ( VOIDFUNCPTR * vector, /* exception vector to attach to */ VOIDFUNCPTR routine /* routine to be called */ ) { INSTR * newVector; newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); #if (CPU != PPC403) if (!excExtendedStubs) { #endif bcopy ((char *) excConnectCode, (char *) newVector, sizeof(excConnectCode)); newVector[ENT_OFF] = (INSTR) (0x48000003 | (((int)excEnt) & 0x03ffffff)); newVector[ISR_OFF] = (INSTR) (0x48000003 | (((int)routine) & 0x03ffffff)); newVector[EXIT_OFF] = (INSTR) (0x48000003 | (((int)excExit) & 0x03ffffff)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excConnectCode)); #if (CPU != PPC403) } else { bcopy ((char *) excExtConnectCode, (char *) newVector, sizeof(excExtConnectCode)); newVector[EXT_ENT_OFF] = (INSTR) (0x3c600000 | MSW((int)excEnt)); newVector[EXT_ENT_OFF+1] = (INSTR) (0x60630000 | LSW((int)excEnt)); newVector[EXT_ISR_OFF] = (INSTR) (0x3c600000 | MSW((int)routine)); newVector[EXT_ISR_OFF+1] = (INSTR) (0x60630000 | LSW((int)routine)); newVector[EXT_EXIT_OFF] = (INSTR) (0x3c600000 | MSW((int)excExit)); newVector[EXT_EXIT_OFF+1] = (INSTR) (0x60630000 | LSW((int)excExit)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excExtConnectCode)); } #endif return (OK); } /******************************************************************************* * * excIntConnect - connect a C routine to an asynchronous exception vector * * This routine connects a specified C routine to a specified asynchronous * exception vector, such as the external interrupt vector (0x500) and the * decrementer vector (0x900). An interrupt stub is created and placed at * in the exception table. The address of is stored in the * interrupt stub code. When the asynchronous exception occurs, the processor * jumps to the interrupt stub code, saves only the requested registers, and * calls the C routines. * * When the C routine is invoked, interrupts are still locked. It is the * C routine responsibility to re-enable interrupts. * * The routine can be any normal C code, except that it must not * invoke certain operating system functions that may block or perform * I/O operations. * * Before the requested registers are saved, the interrupt stub switches from the * current task stack to the interrupt stack. In the case of nested interrupts, no * stack switching is performed, because the interrupt is already set. * * RETURNS: OK, always. * * SEE ALSO: excConnect(), excVecSet() */ STATUS excIntConnect ( VOIDFUNCPTR * vector, /* exception vector to attach to */ VOIDFUNCPTR routine /* routine to be called */ ) { INSTR * newVector; newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); #if (CPU != PPC403) if (!excExtendedStubs) { #endif bcopy ((char *) excConnectCode, (char *) newVector, sizeof(excConnectCode)); newVector[ENT_OFF] = (INSTR) (0x48000003 | (((int)intEnt) & 0x03ffffff)); newVector[ISR_OFF] = (INSTR) (0x48000003 | (((int)routine) & 0x03ffffff)); newVector[EXIT_OFF] = (INSTR) (0x48000003 | (((int)intExit) & 0x03ffffff)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excConnectCode)); #if (CPU != PPC403) } else { bcopy ((char *) excExtConnectCode, (char *) newVector, sizeof(excExtConnectCode)); newVector[EXT_ENT_OFF] = (INSTR) (0x3c600000 | MSW((int)intEnt)); newVector[EXT_ENT_OFF+1] = (INSTR) (0x60630000 | LSW((int)intEnt)); newVector[EXT_ISR_OFF] = (INSTR) (0x3c600000 | MSW((int)routine)); newVector[EXT_ISR_OFF+1] = (INSTR) (0x60630000 | LSW((int)routine)); newVector[EXT_EXIT_OFF] = (INSTR) (0x3c600000 | MSW((int)intExit)); newVector[EXT_EXIT_OFF+1] = (INSTR) (0x60630000 | LSW((int)intExit)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excExtConnectCode)); } #endif return (OK); } #if (CPU == PPC403) /******************************************************************************* * * excCrtConnect - connect a C routine to a critical exception vector (PowerPC 403) * * This routine connects a specified C routine to a specified critical exception * vector. An exception stub is created and in placed at in the * exception table. The address of is stored in the exception stub * code. When an exception occurs, the processor jumps to the exception stub * code, saves the registers, and call the C routines. * * The routine can be any normal C code, except that it must not * invoke certain operating system functions that may block or perform * I/O operations. * * The registers are saved to an Exception Stack Frame (ESF) which is placed * on the stack of the task that has produced the exception. The ESF structure * is defined in h/arch/ppc/esfPpc.h. * * The only argument passed by the exception stub to the C routine is a pointer * to the ESF containing the register values. The prototype of this C routine * is as follows: * .CS * void excHandler (ESFPPC *); * .CE * * When the C routine returns, the exception stub restores the registers saved * in the ESF and continues execution of the current task. * * RETURNS: OK, always. * * SEE ALSO: excIntConnect(), excIntCrtConnect, excVecSet() */ STATUS excCrtConnect ( VOIDFUNCPTR * vector, /* exception vector to attach to */ VOIDFUNCPTR routine /* routine to be called */ ) { INSTR * newVector; newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); bcopy ((char *) excCrtConnectCode, (char *) newVector, sizeof(excCrtConnectCode)); newVector[ENT_CRT_OFF] = (INSTR) (0x48000003 | (((int ) excCrtEnt) & 0x03ffffff)); newVector[ISR_CRT_OFF] = (INSTR) (0x48000003 | (((int ) routine) & 0x03ffffff)); newVector[EXIT_CRT_OFF] = (INSTR) (0x48000003 | (((int ) excCrtExit) & 0x03ffffff)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excCrtConnectCode)); return (OK); } /******************************************************************************* * * excIntCrtConnect - connect a C routine to a critical interrupt vector (PowerPC 403) * * This routine connects a specified C routine to a specified asynchronous * critical exception vector such as the critical external interrupt vector * (0x100), or the watchdog timer vector (0x1020). An interrupt stub is created * and placed at in the exception table. The address of is * stored in the interrupt stub code. When the asynchronous exception occurs, * the processor jumps to the interrupt stub code, saves only the requested * registers, and calls the C routines. * * When the C routine is invoked, interrupts are still locked. It is the * C routine's responsibility to re-enable interrupts. * * The routine can be any normal C routine, except that it must not * invoke certain operating system functions that may block or perform * I/O operations. * * Before the requested registers are saved, the interrupt stub switches from the * current task stack to the interrupt stack. In the case of nested interrupts, no * stack switching is performed, because the interrupt stack is already set. * * RETURNS: OK, always. * * SEE ALSO: excConnect(), excCrtConnect, excVecSet() */ STATUS excIntCrtConnect ( VOIDFUNCPTR * vector, /* exception vector to attach to */ VOIDFUNCPTR routine /* routine to be called */ ) { INSTR * newVector; newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); bcopy ((char *) excCrtConnectCode, (char *) newVector, sizeof(excCrtConnectCode)); newVector[ENT_CRT_OFF] = (INSTR) (0x48000003 | (((int ) intCrtEnt) & 0x03ffffff)); newVector[ISR_CRT_OFF] = (INSTR) (0x48000003 | (((int ) routine) & 0x03ffffff)); newVector[EXIT_CRT_OFF] = (INSTR) (0x48000003 | (((int ) intCrtExit) & 0x03ffffff)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excCrtConnectCode)); return (OK); } /******************************************************************************* * * excIntConnectTimer - connect a C routine to the FIT or PIT interrupt vector * */ STATUS excIntConnectTimer ( VOIDFUNCPTR * vector, /* exception vector to attach to */ VOIDFUNCPTR routine /* routine to be called */ ) { INSTR * newVector; INSTR *pCode; /* pointer to newly synthesized code */ /* construct an interrupt handler for a C routine */ if ((int) vector == (int)_EXC_OFF_PIT) pCode = (INSTR *) _EXC_NEW_OFF_PIT; else if ((int) vector == (int)_EXC_OFF_FIT) pCode = (INSTR *) _EXC_NEW_OFF_FIT; else return (ERROR); if (pCode != NULL) { /* copy excConnectCode into new code area */ bcopy ((char *)excConnectCode, (char *)pCode, sizeof (excConnectCode)); pCode[ENT_OFF] = (INSTR) (0x48000003 | (((int ) intEnt) & 0x03ffffff)); pCode[ISR_OFF] = (INSTR) (0x48000003 | (((int ) routine) & 0x03ffffff)); pCode[EXIT_OFF]= (INSTR) (0x48000003 | (((int ) intExit) & 0x03ffffff)); } /* make vector point to synthesized code */ newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); bcopy ((char *) excConnectCodeTimer, (char *) newVector, sizeof(excConnectCodeTimer)); newVector[0] = (INSTR) (0x48000002 | (((int ) pCode) & 0x03fffffe)); CACHE_TEXT_UPDATE((void *) pCode, sizeof (excConnectCode)); CACHE_TEXT_UPDATE((void *) newVector, sizeof (excConnectCodeTimer)); return (OK); } #endif /* (CPU == PPC403) */ /******************************************************************************* * * excVecSet - set a CPU exception vector * * This routine set the C routine that will be called when the exception * corresponding to will occur. This function doesn't create the * exception stub. Just replace the C routine to call in the exception * stub. * * SEE ALSO: excVecGet(), excConnect(), excIntConnect() */ void excVecSet ( FUNCPTR * vector, /* vector offset */ FUNCPTR function /* address to place in vector */ ) { INSTR * newVector; /* vector is offset by the vector base address */ newVector = (INSTR *) ((int) vector | (int) excVecBaseGet ()); #if (CPU != PPC403) if (!excExtendedStubs) { #endif newVector[ISR_OFF] = (INSTR) (0x48000003 | (((int)function) & 0x03ffffff)); CACHE_TEXT_UPDATE((void *) &newVector[ISR_OFF], sizeof (INSTR *)); #if (CPU != PPC403) } else { newVector[EXT_ISR_OFF] = (INSTR) (0x3c600000 | MSW((int)function)); newVector[EXT_ISR_OFF+1] = (INSTR) (0x60630000 | LSW((int)function)); CACHE_TEXT_UPDATE((void *) &newVector[EXT_ISR_OFF], 2 * sizeof (INSTR *)); } #endif } /******************************************************************************* * * excVecGet - get a CPU exception vector * * This routine returns the address of the current C routine connected to * the . * * RETURNS: the address of the C routine. * * SEE ALSO: excVecSet() */ FUNCPTR excVecGet ( FUNCPTR * vector /* vector offset */ ) { INSTR * vec; INSTR routine; /* vector is offset by vector base address */ vec = (INSTR *) ((int) vector | (int) excVecBaseGet ()); #if (CPU != PPC403) if (!excExtendedStubs) { #endif /* extract the routine address from the instruction */ routine = vec[ISR_OFF] & 0x03fffffc; /* extend the sign if necessary */ if (routine & 0x02000000) routine |= 0xfc000000; #if (CPU != PPC403) } else { /* extract the routine address from the instructions */ routine = (vec[EXT_ISR_OFF] << 16) | (vec[EXT_ISR_OFF+1] & 0x0000ffff); } #endif return ((FUNCPTR) routine); } /******************************************************************************* * * excVecBaseSet - set the exception vector base address * * This routine sets the vector base address. The MSR's IP bit is set to zero * or one according to the specified value, and subsequent calls to excVecGet() * or excVecSet() will use this base address. The vector base address is * initially 0, until changed by calls to this routine. * * NOTE: * PowerPC does not have a vector base register. However, the IP or EP bit in * the machine state register set the prefix of exception vector. Thus the * vector base can only be set to 0x00000000 or 0xfff00000. * * RETURNS: N/A * * SEE ALSO: excVecBaseGet(), excVecGet(), excVecSet() */ void excVecBaseSet ( FUNCPTR * baseAddr /* new vector base address */ ) { #if (CPU == PPC403) excVecBase = baseAddr; vxEvprSet ((int) baseAddr); #else /* (CPU == PPC403) */ if ((int) baseAddr == _PPC_EXC_VEC_BASE_LOW || (int) baseAddr == _PPC_EXC_VEC_BASE_HIGH) { excVecBase = baseAddr; /* keep the base address in a static variable */ excEPSet (baseAddr); /* set the actual vector base register */ } #endif /* (CPU == PPC403) */ } /******************************************************************************* * * excVecBaseGet - get the vector base address * * This routine returns the current vector base address that has been set * with the intVecBaseSet() routine. * * RETURNS: The current vector base address. * * SEE ALSO: intVecBaseSet() */ FUNCPTR * excVecBaseGet (void) { return (excVecBase); } /******************************************************************************* * * * excExcHandle - interrupt level handling of exceptions * * This routine handles exception traps. It is never be called except * from the special assembly language interrupt stub routine. * * It prints out a bunch of pertinent information about the trap that * occurred via excTask. * * Note that this routine runs in the context of the task that got the exception. * * NOMANUAL */ void excExcHandle ( ESFPPC * pEsf /* pointer to exception stack frame */ ) { EXC_INFO excInfo; int vecNum = pEsf->vecOffset; /* exception vector number */ REG_SET * pRegs = &pEsf->regSet; /* pointer to register on esf */ #ifdef WV_INSTRUMENTATION /* windview - level 3 event logging */ EVT_CTX_1(EVENT_EXCEPTION, vecNum); #endif #if (CPU == PPC403) if (((*(INSTR *) pEsf->regSet.pc) == DBG_BREAK_INST) && (_func_excTrapRtn != NULL)) { (* _func_excTrapRtn) (pEsf->regSet.pc , pRegs, pEsf, NULL, FALSE); } else #elif ((CPU == PPC603) || (CPU == PPCEC603) || (CPU == PPC604) || \ (CPU == PPC860)) if ((pEsf->regSet.msr & _EXC_PROG_SRR1_TRAP) && (_func_excTrapRtn != NULL) && ((*(INSTR *) pEsf->regSet.pc) == DBG_BREAK_INST)) { pEsf->regSet.msr &= ~_EXC_PROG_SRR1_TRAP; (* _func_excTrapRtn) (pEsf, pRegs, NULL, FALSE); } else #endif /* ((CPU == PPC603) || (CPU == PPC604) || (CPU == PPC860)) */ { excGetInfoFromESF (vecNum, pEsf, &excInfo); if ((_func_excBaseHook != NULL) && /* user hook around? */ ((* _func_excBaseHook) (vecNum, pEsf, pRegs, &excInfo))) return; /* user hook fixed it */ if (INT_CONTEXT ()) { if (_func_excPanicHook != NULL) /* panic hook? */ (*_func_excPanicHook) (vecNum, pEsf, pRegs, &excInfo); reboot (BOOT_WARM_AUTOBOOT); return; /* reboot returns?! */ } /* task caused exception */ taskIdCurrent->pExcRegSet = pRegs; /* for taskRegs[GS]et */ taskIdDefault ((int)taskIdCurrent); /* update default tid */ bcopy ((char *) &excInfo, (char *) &(taskIdCurrent->excInfo), sizeof (EXC_INFO)); /* copy in exc info */ if (_func_sigExcKill != NULL) _func_sigExcKill((int) vecNum, vecNum, pRegs); if (_func_excInfoShow != NULL) /* default show rtn? */ (*_func_excInfoShow) (&excInfo, TRUE); if (excExcepHook != NULL) (* excExcepHook) (taskIdCurrent, vecNum, pEsf); taskSuspend (0); /* whoa partner... */ taskIdCurrent->pExcRegSet = (REG_SET *) NULL; /* invalid after rts */ } } /******************************************************************************* * * excIntHandle - interrupt level handling of interrupts * * This routine handles interrupts. It is never to be called except * from the special assembly language interrupt stub routine. * * It prints out a bunch of pertinent information about the trap that * occurred, via excTask. * * NOMANUAL */ void excIntHandle () { #ifdef WV_INSTRUMENTATION /* * windview - level 3 event is not logged here, since this * function is not used for the moment */ #if FALSE EVT_CTX_1(EVENT_EXCEPTION, vecNum); #endif #endif if (_func_excIntHook != NULL) (*_func_excIntHook) (); if (_func_logMsg != NULL) _func_logMsg ("Uninitialized interrupt\n", 0,0,0,0,0,0); } /***************************************************************************** * * excGetInfoFromESF - get relevent info from exception stack frame * * RETURNS: size of specified ESF */ LOCAL int excGetInfoFromESF ( FAST int vecNum, FAST ESFPPC *pEsf, EXC_INFO *pExcInfo ) { pExcInfo->vecOff = vecNum; pExcInfo->cia = pEsf->regSet.pc; /* copy cia */ pExcInfo->msr = pEsf->regSet.msr; /* copy msr */ pExcInfo->cr = pEsf->regSet.cr; /* copy msr */ switch (vecNum) { case _EXC_OFF_MACH: #if (CPU == PPC403) pExcInfo->bear = pEsf->bear; pExcInfo->besr = pEsf->besr; pExcInfo->valid = (_EXC_INFO_DEFAULT | _EXC_INFO_NIA | _EXC_INFO_BEAR | _EXC_INFO_BESR) & ~_EXC_INFO_CIA; #elif (CPU == PPC860) pExcInfo->dsisr = pEsf->dsisr; pExcInfo->dar = pEsf->dar; pExcInfo->valid = _EXC_INFO_DEFAULT | _EXC_INFO_DSISR | _EXC_INFO_DAR; #else pExcInfo->valid = (_EXC_INFO_DEFAULT | _EXC_INFO_NIA) & ~_EXC_INFO_CIA; #endif /* (CPU == PPC403) */ break; #if ((CPU == PPC603) || (CPU == PPCEC603) || (CPU == PPC604) || \ (CPU == PPC860)) case _EXC_OFF_DATA: pExcInfo->dsisr = pEsf->dsisr; pExcInfo->dar = pEsf->dar; pExcInfo->valid = _EXC_INFO_DEFAULT | _EXC_INFO_DSISR | _EXC_INFO_DAR; break; case _EXC_OFF_INST: /* XXX pExcInfo->valid = _EXC_INFO_DEFAULT | _EXC_INFO_NIA; */ pExcInfo->valid = (_EXC_INFO_DEFAULT | _EXC_INFO_NIA) & ~_EXC_INFO_CIA; break; case _EXC_OFF_FPU: break; #endif /* ((CPU == PPC603) || (CPU == PPC604) || (CPU == PPC860)) */ case _EXC_OFF_PROG: #ifdef _PPC_MSR_FP if (((taskIdCurrent->options & VX_FP_TASK) != 0) && ((vxMsrGet() & _PPC_MSR_FP) != 0)) { /* get the floating point status and control register */ pEsf->fpcsr = vxFpscrGet(); pExcInfo->fpcsr = pEsf->fpcsr; } #endif /* _PPC_MSR_FP */ pExcInfo->valid = _EXC_INFO_DEFAULT; break; case _EXC_OFF_ALIGN: #if (CPU == PPC403) pExcInfo->dear = pEsf->dear; pExcInfo->valid = _EXC_INFO_DEFAULT | _EXC_INFO_DEAR; #else pExcInfo->dsisr = pEsf->dsisr; pExcInfo->dar = pEsf->dar; pExcInfo->valid = _EXC_INFO_DEFAULT | _EXC_INFO_DSISR | _EXC_INFO_DAR; #endif /* (CPU == PPC403) */ break; case _EXC_OFF_SYSCALL: pExcInfo->valid = _EXC_INFO_DEFAULT; break; #if (CPU == PPC601) case _EXC_OFF_IOERR: break; case _EXC_OFF_RUN_TRACE: #elif ((CPU == PPC603) || (CPU == PPCEC603) || (CPU == PPC604) || \ (CPU == PPC860)) case _EXC_OFF_TRACE: #if ((CPU == PPC603) || (CPU == PPCEC603)) case _EXC_OFF_INST_MISS: case _EXC_OFF_LOAD_MISS: case _EXC_OFF_STORE_MISS: #elif (CPU == PPC604) case _EXC_OFF_INST_BRK: case _EXC_OFF_SYS_MNG: #elif (CPU == PPC860) case _EXC_OFF_SW_EMUL: case _EXC_OFF_INST_MISS: case _EXC_OFF_DATA_MISS: case _EXC_OFF_INST_ERROR: case _EXC_OFF_DATA_ERROR: case _EXC_OFF_DATA_BKPT: case _EXC_OFF_INST_BKPT: case _EXC_OFF_PERI_BKPT: case _EXC_OFF_NM_DEV_PORT: #endif /* (CPU == PPC603) */ #elif (CPU == PPC403) case _EXC_OFF_PROT: case _EXC_OFF_WD: case _EXC_OFF_DBG: case _EXC_OFF_INST: case _EXC_OFF_INST_MISS: case _EXC_OFF_DATA_MISS: pExcInfo->valid = _EXC_INFO_DEFAULT; break; #endif /* (CPU == PPC403) */ default: pExcInfo->valid = _EXC_INFO_DEFAULT; break; } return (sizeof (ESFPPC)); }