diff options
Diffstat (limited to 'cfe/cfe/arch/mips/common/src/exchandler.c')
-rw-r--r-- | cfe/cfe/arch/mips/common/src/exchandler.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/cfe/cfe/arch/mips/common/src/exchandler.c b/cfe/cfe/arch/mips/common/src/exchandler.c new file mode 100644 index 0000000..d69b980 --- /dev/null +++ b/cfe/cfe/arch/mips/common/src/exchandler.c @@ -0,0 +1,580 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Exception Handler File: exchandler.c + * + * This is the "C" part of the exception handler and the + * associated setup routines. We call these routines from + * the assembly-language exception handler. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "sbmips.h" +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "exception.h" +#include "cfe.h" +#include "cfe_error.h" +#include "cfe_iocb.h" +#include "exchandler.h" +#include "cpu_config.h" +#include "bsp_config.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +/* + * Temporary until all our CPU packages support a cache error handler + */ + +#ifndef CPUCFG_CERRHANDLER +#define CPUCFG_CERRHANDLER 0xBFC00000 +#else +extern void CPUCFG_CERRHANDLER(void); +#endif + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +exc_handler_t exc_handler; +extern void _exc_entry(void); +extern void _exc_setup_locore(long); +extern void CPUCFG_TLBHANDLER(void); +extern void cfe_flushcache(uint32_t,long,long); +extern uint32_t _getstatus(void); +extern void _setstatus(uint32_t); + +static const char *regnames = "0 ATv0v1a0a1a2a3t0t1t2t3t4t5t6t7" + "s0s1s2s3s4s5s6s7t8t9k0k1gpspfpra"; +static const char *excnames = + "Interrupt" /* 0 */ + "TLBMod " /* 1 */ + "TLBMissRd" /* 2 */ + "TLBMissWr" /* 3 */ + "AddrErrRd" /* 4 */ + "AddrErrWr" /* 5 */ + "BusErrRd " /* 6 */ + "BusErrWr " /* 7 */ + "Syscall " /* 8 */ + "Breakpt " /* 9 */ + "InvOpcode" /* 10 */ + "CoProcUnu" /* 11 */ + "ArithOvfl" /* 12 */ + "TrapExc " /* 13 */ + "VCEI " /* 14 */ + "FPUExc " /* 15 */ + "CP2Exc " /* 16 */ + "Exc17 " /* 17 */ + "Exc18 " /* 18 */ + "Exc19 " /* 19 */ + "Exc20 " /* 20 */ + "Exc21 " /* 21 */ + "Exc22 " /* 22 */ + "Watchpt " /* 23 */ + "Exc24 " /* 24 */ + "Exc25 " /* 25 */ + "Exc26 " /* 26 */ + "Exc27 " /* 27 */ + "Exc28 " /* 28 */ + "Exc29 " /* 29 */ + "Exc30 " /* 30 */ + "VCED "; /* 31 */ + + + +/* ********************************************************************* + * cfe_exception(code,info) + * + * Exception handler. This routine is called when any CPU + * exception that is handled by the assembly-language + * vectors is reached. The usual thing to do here is just to + * reboot. + * + * Input parameters: + * code - exception type + * info - exception stack frame + * + * Return value: + * usually reboots + ********************************************************************* */ + +void cfe_exception(int code,uint64_t *info) +{ + int idx; + + SETLEDS("EXC!"); + + if(exc_handler.catch_exc == 1) { + /*Deal with exception without restarting CFE.*/ + + /*Clear relevant SR bits*/ + _exc_clear_sr_exl(); + _exc_clear_sr_erl(); + + /*Reset flag*/ + exc_handler.catch_exc = 0; + + exc_longjmp_handler(); + } + + +#ifdef _MIPSREGS32_ + xprintf("**Exception %d: EPC=%08X, Cause=%08X (%9s)\n", + code,(uint32_t)info[XCP0_EPC], + (uint32_t)info[XCP0_CAUSE], + excnames + G_CAUSE_EXC((uint32_t)info[XCP0_CAUSE])*9); + xprintf(" RA=%08X, VAddr=%08X\n", + (uint32_t)info[XGR_RA],(uint32_t)info[XCP0_VADDR]); + xprintf("\n"); + for (idx = 0;idx < 32; idx+= 2) { + xprintf(" %2s ($%2d) = %08X %2s ($%2d) = %08X\n", + regnames+(idx*2), + idx,(uint32_t)info[XGR_ZERO+idx], + regnames+((idx+1)*2), + idx+1,(uint32_t)info[XGR_ZERO+idx+1]); + } +#else + xprintf("**Exception %d: EPC=%016llX, Cause=%08X (%9s)\n", + code,info[XCP0_EPC],info[XCP0_CAUSE], + excnames + G_CAUSE_EXC((uint32_t)info[XCP0_CAUSE])*9); + xprintf(" RA=%016llX, VAddr=%016llX\n", + info[XGR_RA],info[XCP0_VADDR]); + xprintf("\n"); + for (idx = 0;idx < 32; idx+= 2) { + xprintf(" %2s ($%2d) = %016llX %2s ($%2d) = %016llX\n", + regnames+(idx*2), + idx,info[XGR_ZERO+idx], + regnames+((idx+1)*2), + idx+1,info[XGR_ZERO+idx+1]); + } +#endif + + xprintf("\n"); + _exc_restart(); +} + +#if (!CFG_BOOTRAM) && (CFG_RUNFROMKSEG0) +/* ********************************************************************* + * exc_setup_hw_vector(vecoffset,target,k0code) + * + * Install a patch of code at the specified offset in low + * KSEG0 memory that will jump to 'target' and load k0 + * with the specified code value. This is used when we + * run with RAM vectors. + * + * Input parameters: + * vecoffset - offset into KSEG0 + * target - location where we should branch when vector is called + * k0code - value to load into K0 before branching + * + * Return value: + * nothing + ********************************************************************* */ + +static void exc_setup_hw_vector(uint32_t vecoffset, + void *target, + uint32_t k0code) +{ + uint32_t *vec; + uint32_t new; + uint32_t lower,upper; + + new = (uint32_t) (intptr_t) target; /* warning: assumes compatibility addresses! */ + + lower = new & 0xffff; + upper = (new >> 16) & 0xffff; + if ((lower & 0x8000) != 0) { + upper++; + } + + /* + * Get a KSEG0 version of the vector offset. + */ + vec = (uint32_t *) PHYS_TO_K0(vecoffset); + + /* + * Patch in the vector. Note that we have to flush + * the L1 Dcache and invalidate the L1 Icache before + * we can use this. + */ + + vec[0] = 0x3c1b0000 | upper; /* lui k1, HIGH(new) */ + vec[1] = 0x277b0000 | lower; /* addiu k1, k1, LOW(new) */ + vec[2] = 0x03600008; /* jr k1 */ + vec[3] = 0x241a0000 | k0code; /* li k0, code */ + +} + + +/* ********************************************************************* + * exc_install_ram_vectors() + * + * Install all of the hardware vectors into low memory, + * flush the cache, and clear the BEV bit so we can start + * using them. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +static void exc_install_ram_vectors(void) +{ + uint32_t *ptr; + int idx; + + /* Debug: blow away the vector area so we can see what we did */ + ptr = (uint32_t *) PHYS_TO_K0(0); + for (idx = 0; idx < 0x1000/sizeof(uint32_t); idx++) *ptr++ = 0; + + /* + * Set up the vectors. The cache error handler is set up + * specially. + */ + + exc_setup_hw_vector(MIPS_RAM_VEC_TLBFILL, CPUCFG_TLBHANDLER,XTYPE_TLBFILL); + exc_setup_hw_vector(MIPS_RAM_VEC_XTLBFILL, _exc_entry,XTYPE_XTLBFILL); + exc_setup_hw_vector(MIPS_RAM_VEC_CACHEERR, _exc_entry,XTYPE_CACHEERR); + exc_setup_hw_vector(MIPS_RAM_VEC_EXCEPTION,_exc_entry,XTYPE_EXCEPTION); + exc_setup_hw_vector(MIPS_RAM_VEC_INTERRUPT,_exc_entry,XTYPE_INTERRUPT); + + /* + * Flush the D-cache and invalidate the I-cache so we can start + * using these vectors. + */ + + cfe_flushcache(CFE_CACHE_FLUSH_D | CFE_CACHE_INVAL_I,0,0); + + /* + * Write the handle into our low memory space. If we need to save + * other stuff down there, this is a good place to do it. + * This call uses uncached writes - we have not touched the + * memory in the handlers just yet, so they should not be + * in our caches. + */ + + _exc_setup_locore((intptr_t) CPUCFG_CERRHANDLER); + + /* + * Finally, clear BEV so we'll use the vectors in RAM. + */ + + _setstatus(_getstatus() & ~M_SR_BEV); + + /* + * XXX There's a hazard here, but we're not going to worry about + * XXX it. It is unlikely we'll use the vectors any time soon. + */ +} +#endif + +/* ********************************************************************* + * cfe_setup_exceptions() + * + * Set up the exception handlers. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ +void cfe_setup_exceptions(void) +{ + _exc_setvector(XTYPE_TLBFILL, (void *) cfe_exception); + _exc_setvector(XTYPE_XTLBFILL, (void *) cfe_exception); + _exc_setvector(XTYPE_CACHEERR, (void *) _exc_cache_crash_sim); + _exc_setvector(XTYPE_EXCEPTION,(void *) cfe_exception); + _exc_setvector(XTYPE_INTERRUPT,(void *) cfe_exception); + _exc_setvector(XTYPE_EJTAG, (void *) cfe_exception); + + exc_handler.catch_exc = 0; + q_init( &(exc_handler.jmpbuf_stack)); + +#if (!CFG_BOOTRAM) && (CFG_RUNFROMKSEG0) + /* + * Install RAM vectors, and clear the BEV bit in the status + * register. Don't do this if we're running from PromICE RAM + */ + exc_install_ram_vectors(); +#endif +} + + + +/* ********************************************************************* + * exc_initialize_block() + * + * Set up the exception handler. Allow exceptions to be caught. + * Allocate memory for jmpbuf and store it away. + * + * Returns NULL if error in memory allocation. + * + * Input parameters: + * nothing + * + * Return value: + * jmpbuf_t structure, or NULL if no memory + ********************************************************************* */ +jmpbuf_t *exc_initialize_block(void) +{ + jmpbuf_t *jmpbuf_local; + + exc_handler.catch_exc = 1; + + /* Create the jmpbuf_t object */ + jmpbuf_local = (jmpbuf_t *) KMALLOC((sizeof(jmpbuf_t)),0); + + if (jmpbuf_local == NULL) { + return NULL; + } + + q_enqueue( &(exc_handler.jmpbuf_stack), &((*jmpbuf_local).stack)); + + return jmpbuf_local; +} + +/* ********************************************************************* + * exc_cleanup_block(dq_jmpbuf) + * + * Remove dq_jmpbuf from the exception handler stack and free + * the memory. + * + * Input parameters: + * dq_jmpbuf - block to deallocate + * + * Return value: + * nothing + ********************************************************************* */ + +void exc_cleanup_block(jmpbuf_t *dq_jmpbuf) +{ + int count; + + if (dq_jmpbuf == NULL) { + return; + } + + count = q_count( &(exc_handler.jmpbuf_stack)); + + if( count > 0 ) { + q_dequeue( &(*dq_jmpbuf).stack ); + KFREE(dq_jmpbuf); + } +} + +/* ********************************************************************* + * exc_cleanup_handler(dq_jmpbuf,chain_exc) + * + * Clean a block, then chain to the next exception if required. + * + * Input parameters: + * dq_jmpbuf - current exception + * chain_exc - true if we should chain to the next handler + * + * Return value: + * nothing + ********************************************************************* */ + +void exc_cleanup_handler(jmpbuf_t *dq_jmpbuf, int chain_exc) +{ + exc_cleanup_block(dq_jmpbuf); + + if( chain_exc == EXC_CHAIN_EXC ) { + /*Go to next exception on stack */ + exc_longjmp_handler(); + } +} + + + +/* ********************************************************************* + * exc_longjmp_handler() + * + * This routine long jumps to the exception handler on the top + * of the exception stack. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ +void exc_longjmp_handler(void) +{ + int count; + jmpbuf_t *jmpbuf_local; + + count = q_count( &(exc_handler.jmpbuf_stack)); + + if( count > 0 ) { + jmpbuf_local = (jmpbuf_t *) q_getlast(&(exc_handler.jmpbuf_stack)); + + SETLEDS("CFE "); + + lib_longjmp( (*jmpbuf_local).jmpbuf, -1); + } +} + + +/* ********************************************************************* + * mem_peek(d,addr,type) + * + * Read memory of the specified type at the specified address. + * Exceptions are caught in the case of a bad memory reference. + * + * Input parameters: + * d - pointer to where data should be placed + * addr - address to read + * type - type of read to do (MEM_BYTE, etc.) + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int mem_peek(void *d, long addr, int type) +{ + + jmpbuf_t *jb; + + jb = exc_initialize_block(); + if( jb == NULL ) { + return CFE_ERR_NOMEM; + } + + if (exc_try(jb) == 0) { + + switch (type) { + case MEM_BYTE: + *(uint8_t *)d = *((volatile uint8_t *) addr); + break; + case MEM_HALFWORD: + *(uint16_t *)d = *((volatile uint16_t *) addr); + break; + case MEM_WORD: + *(uint32_t *)d = *((volatile uint32_t *) addr); + break; + case MEM_QUADWORD: + *(uint64_t *)d = *((volatile uint64_t *) addr); + break; + default: + return CFE_ERR_INV_PARAM; + } + + exc_cleanup_block(jb); + } + else { + /*Exception handler*/ + + exc_cleanup_handler(jb, EXC_NORMAL_RETURN); + return CFE_ERR_GETMEM; + } + + return 0; +} + +/* ********************************************************************* + * Write memory of type at address addr with value val. + * Exceptions are caught, handled (error message) and function + * returns with 0. + * + * 1 success + * 0 failure + ********************************************************************* */ + +int mem_poke(long addr, uint64_t val, int type) +{ + + jmpbuf_t *jb; + + jb = exc_initialize_block(); + if( jb == NULL ) { + return CFE_ERR_NOMEM; + } + + if (exc_try(jb) == 0) { + + switch (type) { + case MEM_BYTE: + *((volatile uint8_t *) addr) = (uint8_t) val; + break; + case MEM_HALFWORD: + *((volatile uint16_t *) addr) = (uint16_t) val; + break; + case MEM_WORD: + *((volatile uint32_t *) addr) = (uint32_t) val; + break; + case MEM_QUADWORD: + *((volatile uint64_t *) addr) = (uint64_t) val; + break; + default: + return CFE_ERR_INV_PARAM; + } + + exc_cleanup_block(jb); + } + else { + /*Exception handler*/ + + exc_cleanup_handler(jb, EXC_NORMAL_RETURN); + return CFE_ERR_SETMEM; + } + + return 0; +} + + + + + + + + + + |