blob: ed5408cdc521419098fc89b895df3d79811012a0 [file] [log] [blame]
/*
* Copyright 2014-2015, Imagination Technologies Limited and/or its
* affiliated group companies.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY 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)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <mips/cpu.h>
#include <mips/fpa.h>
#include <mips/hal.h>
#include <mips/uhi_syscalls.h>
/* Defined in .ld file */
extern char __use_excpt_boot[];
extern char __attribute__((weak)) __flush_to_zero[];
#ifdef VERBOSE_EXCEPTIONS
/*
* Write a string, a formatted number, then a string.
*/
static void
putsnds (const char *pre, reg_t value, int digits, const char *post)
{
char buf[digits];
int shift;
int idx = 0;
if (pre != NULL)
write (1, pre, strlen (pre));
for (shift = ((digits - 1) * 4) ; shift >= 0 ; shift -= 4)
buf[idx++] = "0123456789ABCDEF"[(value >> shift) & 0xf];
write (1, buf, digits);
if (post != NULL)
write (1, post, strlen (post));
}
static void
putsns (const char *pre, reg_t value, const char *post)
{
putsnds (pre, value, sizeof (reg_t) * 2, post);
}
# define WRITE(MSG) write (1, (MSG), strlen (MSG))
# define PUTSNDS(PRE, VALUE, DIGITS, POST) \
putsnds ((PRE), (VALUE), (DIGITS), (POST))
# define PUTSNS(PRE, VALUE, POST) \
putsns ((PRE), (VALUE), (POST))
#else
# define WRITE(MSG)
# define PUTSNDS(PRE, VALUE, DIGITS, POST)
# define PUTSNS(PRE, VALUE, POST)
#endif // !VERBOSE_EXCEPTIONS
/* Handle an exception */
#ifdef VERBOSE_EXCEPTIONS
void __attribute__((nomips16))
__exception_handle_verbose (struct gpctx *ctx, int exception)
#else
void __attribute__((nomips16))
__exception_handle_quiet (struct gpctx *ctx, int exception)
#endif
{
switch (exception)
{
case EXC_MOD:
WRITE ("TLB modification exception\n");
break;
case EXC_TLBL:
PUTSNS ("TLB error on load from 0x", ctx->badvaddr, NULL);
PUTSNS (" @0x", ctx->epc, "\n");
break;
case EXC_TLBS:
PUTSNS ("TLB error on store to 0x", ctx->badvaddr, NULL);
PUTSNS (" @0x", ctx->epc, "\n");
break;
case EXC_ADEL:
PUTSNS ("Address error on load from 0x", ctx->badvaddr, NULL);
PUTSNS (" @0x", ctx->epc, "\n");
break;
case EXC_ADES:
PUTSNS ("Address error on store to 0x", ctx->badvaddr, NULL);
PUTSNS (" @0x", ctx->epc, "\n");
break;
case EXC_IBE:
WRITE ("Instruction bus error\n");
break;
case EXC_DBE:
WRITE ("Data bus error\n");
break;
case EXC_SYS:
/* Process a UHI SYSCALL, all other SYSCALLs should have been processed
by our caller. __use_excpt_boot has following values:
0 = Do not use exception handler present in boot.
1 = Use exception handler present in boot if BEV
is 0 at startup.
2 = Always use exception handler present in boot. */
/* Special handling for boot/low level failures. */
if (ctx->t2[1] == __MIPS_UHI_BOOTFAIL)
{
switch (ctx->a[0])
{
case __MIPS_UHI_BF_CACHE:
WRITE ("L2 cache configuration error\n");
break;
default:
WRITE ("Unknown boot failure error\n");
break;
}
/* These are unrecoverable. Abort. */
ctx->epc = (sreg_t)(long)&__exit;
/* Exit code of 255 */
ctx->a[0] = 0xff;
return;
}
if (((long) __use_excpt_boot == 2
|| ((long) __use_excpt_boot == 1
&& __get_startup_BEV
&& __get_startup_BEV () == 0))
&& __chain_uhi_excpt)
/* This will not return. */
__chain_uhi_excpt (ctx);
else
__uhi_indirect (ctx);
return;
case EXC_BP:
PUTSNS ("Breakpoint @0x", ctx->epc, "\n");
break;
case EXC_RI:
PUTSNS ("Illegal instruction @0x", ctx->epc, "\n");
break;
case EXC_CPU:
PUTSNS ("Coprocessor unusable @0x", ctx->epc, "\n");
break;
case EXC_OVF:
WRITE ("Overflow\n");
break;
case EXC_TRAP:
WRITE ("Trap\n");
break;
case EXC_MSAFPE:
#if !(__mips_isa_rev == 6 && defined (__mips_micromips))
if (__flush_to_zero
&& (msa_getsr () & FPA_CSR_UNI_X)
&& (msa_getsr () & FPA_CSR_FS) == 0)
{
unsigned int sr = msa_getsr ();
sr &= ~FPA_CSR_UNI_X;
sr |= FPA_CSR_FS;
msa_setsr (sr);
return;
}
#endif
WRITE ("MSA Floating point error\n");
break;
case EXC_FPE:
/* Turn on flush to zero the first time we hit an unimplemented
operation. If we hit it again then stop. */
if (__flush_to_zero
&& (fpa_getsr () & FPA_CSR_UNI_X)
&& (fpa_getsr () & FPA_CSR_FS) == 0)
{
unsigned int sr = fpa_getsr ();
sr &= ~FPA_CSR_UNI_X;
sr |= FPA_CSR_FS;
fpa_setsr (sr);
return;
}
WRITE ("Floating point error\n");
break;
case EXC_IS1:
WRITE ("Implementation specific exception (16)\n");
break;
case EXC_IS2:
WRITE ("Implementation specific exception (17)\n");
break;
case EXC_C2E:
WRITE ("Precise Coprocessor 2 exception\n");
break;
case EXC_TLBRI:
WRITE ("TLB read inhibit exception\n");
break;
case EXC_TLBXI:
WRITE ("TLB execute inhibit exception\n");
break;
case EXC_MSAU:
PUTSNS ("MSA unusable @0x", ctx->epc, "\n");
break;
case EXC_MDMX:
PUTSNS ("MDMX exception @0x", ctx->epc, "\n");
break;
case EXC_WATCH:
PUTSNS ("Watchpoint @0x", ctx->epc, "\n");
break;
case EXC_MCHECK:
WRITE ("Machine check error\n");
break;
case EXC_THREAD:
WRITE ("Thread exception\n");
break;
case EXC_DSPU:
WRITE ("DSP unusable\n");
break;
case EXC_RES30:
WRITE ("Cache error\n");
break;
default:
PUTSNS ("Unhandled exception ", exception, "\n");
}
/* Dump registers */
PUTSNS (" 0:\t", 0, "\t");
PUTSNS ("at:\t", ctx->at, "\t");
PUTSNS ("v0:\t", ctx->v[0], "\t");
PUTSNS ("v1:\t", ctx->v[1], "\n");
PUTSNS ("a0:\t", ctx->a[0], "\t");
PUTSNS ("a1:\t", ctx->a[1], "\t");
PUTSNS ("a2:\t", ctx->a[2], "\t");
PUTSNS ("a3:\t", ctx->a[3], "\n");
PUTSNS ("t0:\t", ctx->t[0], "\t");
PUTSNS ("t1:\t", ctx->t[1], "\t");
PUTSNS ("t2:\t", ctx->t[2], "\t");
PUTSNS ("t3:\t", ctx->t[3], "\n");
PUTSNS ("t4:\t", ctx->t[4], "\t");
PUTSNS ("t5:\t", ctx->t[5], "\t");
PUTSNS ("t6:\t", ctx->t[6], "\t");
PUTSNS ("t7:\t", ctx->t[7], "\n");
PUTSNS ("s0:\t", ctx->s[0], "\t");
PUTSNS ("s1:\t", ctx->s[1], "\t");
PUTSNS ("s2:\t", ctx->s[2], "\t");
PUTSNS ("s3:\t", ctx->s[3], "\n");
PUTSNS ("s4:\t", ctx->s[4], "\t");
PUTSNS ("s5:\t", ctx->s[5], "\t");
PUTSNS ("s6:\t", ctx->s[6], "\t");
PUTSNS ("s7:\t", ctx->s[7], "\n");
PUTSNS ("t8:\t", ctx->t2[0], "\t");
PUTSNS ("t9:\t", ctx->t2[1], "\t");
PUTSNS ("k0:\t", ctx->k[0], "\t");
PUTSNS ("k1:\t", ctx->k[1], "\n");
PUTSNS ("gp:\t", ctx->gp, "\t");
PUTSNS ("sp:\t", ctx->sp, "\t");
PUTSNS ("fp:\t", ctx->fp, "\t");
PUTSNS ("ra:\t", ctx->ra, "\n");
#if __mips_isa_rev < 6
PUTSNS ("hi:\t", ctx->hi, "\t");
PUTSNS ("lo:\t", ctx->lo, "\n");
#endif
PUTSNS ("epc: \t", ctx->epc, "\n");
PUTSNS ("BadVAddr:\t", ctx->badvaddr, "\n");
PUTSNDS ("Status: \t", ctx->status, 8, "\n");
PUTSNDS ("Cause: \t", ctx->cause, 8, "\n");
PUTSNDS ("BadInstr: \t", ctx->badinstr, 8, "\n");
PUTSNDS ("BadPInstr:\t", ctx->badpinstr, 8, "\n");
/* Raise UHI exception which may or may not return. */
if (__uhi_exception (ctx, UHI_ABI) != 0)
{
/* The exception was acknowledged but not handled. Abort. */
ctx->epc = (sreg_t)(long)&__exit;
/* Exit code of 255 */
ctx->a[0] = 0xff;
}
}