diff options
Diffstat (limited to 'sys/mips/mips/trap.c')
-rw-r--r-- | sys/mips/mips/trap.c | 83 |
1 files changed, 75 insertions, 8 deletions
diff --git a/sys/mips/mips/trap.c b/sys/mips/mips/trap.c index 97374a7..3b632d0 100644 --- a/sys/mips/mips/trap.c +++ b/sys/mips/mips/trap.c @@ -251,6 +251,9 @@ char *access_name[] = { "Store Doubleword" }; +#ifdef CPU_CNMIPS +#include <machine/octeon_cop2.h> +#endif static int allow_unaligned_acc = 1; @@ -410,6 +413,7 @@ trap(struct trapframe *trapframe) char *msg = NULL; intptr_t addr = 0; register_t pc; + int cop; trapdebug_enter(trapframe, 0); @@ -767,28 +771,91 @@ dofault: goto err; break; case T_COP_UNUSABLE: +#ifdef CPU_CNMIPS + cop = (trapframe->cause & MIPS_CR_COP_ERR) >> MIPS_CR_COP_ERR_SHIFT; + /* Handle only COP2 exception */ + if (cop != 2) + goto err; + + addr = trapframe->pc; + /* save userland cop2 context if it has been touched */ + if ((td->td_md.md_flags & MDTD_COP2USED) && + (td->td_md.md_cop2owner == COP2_OWNER_USERLAND)) { + if (td->td_md.md_ucop2) + octeon_cop2_save(td->td_md.md_ucop2); + else + panic("COP2 was used in user mode but md_ucop2 is NULL"); + } + + if (td->td_md.md_cop2 == NULL) { + td->td_md.md_cop2 = octeon_cop2_alloc_ctx(); + if (td->td_md.md_cop2 == NULL) + panic("Failed to allocate COP2 context"); + memset(td->td_md.md_cop2, 0, sizeof(*td->td_md.md_cop2)); + } + + octeon_cop2_restore(td->td_md.md_cop2); + + /* Make userland re-request its context */ + td->td_frame->sr &= ~MIPS_SR_COP_2_BIT; + td->td_md.md_flags |= MDTD_COP2USED; + td->td_md.md_cop2owner = COP2_OWNER_KERNEL; + /* Enable COP2, it will be disabled in cpu_switch */ + mips_wr_status(mips_rd_status() | MIPS_SR_COP_2_BIT); + return (trapframe->pc); +#else goto err; break; +#endif + case T_COP_UNUSABLE + T_USER: + cop = (trapframe->cause & MIPS_CR_COP_ERR) >> MIPS_CR_COP_ERR_SHIFT; + if (cop == 1) { #if !defined(CPU_HAVEFPU) /* FP (COP1) instruction */ - if ((trapframe->cause & MIPS_CR_COP_ERR) == 0x10000000) { log_illegal_instruction("COP1_UNUSABLE", trapframe); i = SIGILL; break; +#else + addr = trapframe->pc; + MipsSwitchFPState(PCPU_GET(fpcurthread), td->td_frame); + PCPU_SET(fpcurthread, td); + td->td_frame->sr |= MIPS_SR_COP_1_BIT; + td->td_md.md_flags |= MDTD_FPUSED; + goto out; +#endif + } +#ifdef CPU_CNMIPS + else if (cop == 2) { + addr = trapframe->pc; + if ((td->td_md.md_flags & MDTD_COP2USED) && + (td->td_md.md_cop2owner == COP2_OWNER_KERNEL)) { + if (td->td_md.md_cop2) + octeon_cop2_save(td->td_md.md_cop2); + else + panic("COP2 was used in kernel mode but md_cop2 is NULL"); + } + + if (td->td_md.md_ucop2 == NULL) { + td->td_md.md_ucop2 = octeon_cop2_alloc_ctx(); + if (td->td_md.md_ucop2 == NULL) + panic("Failed to allocate userland COP2 context"); + memset(td->td_md.md_ucop2, 0, sizeof(*td->td_md.md_ucop2)); + } + + octeon_cop2_restore(td->td_md.md_ucop2); + + td->td_frame->sr |= MIPS_SR_COP_2_BIT; + td->td_md.md_flags |= MDTD_COP2USED; + td->td_md.md_cop2owner = COP2_OWNER_USERLAND; + goto out; } #endif - if ((trapframe->cause & MIPS_CR_COP_ERR) != 0x10000000) { + else { log_illegal_instruction("COPn_UNUSABLE", trapframe); i = SIGILL; /* only FPU instructions allowed */ break; } - addr = trapframe->pc; - MipsSwitchFPState(PCPU_GET(fpcurthread), td->td_frame); - PCPU_SET(fpcurthread, td); - td->td_frame->sr |= MIPS_SR_COP_1_BIT; - td->td_md.md_flags |= MDTD_FPUSED; - goto out; case T_FPE: #if !defined(SMP) && (defined(DDB) || defined(DEBUG)) |