target-mips: change interrupt bits to be mips16-aware

We need to stash the operating mode into the low bit of the error PC and
restore it on return from interrupts.

Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Nathan Froyd 2009-12-08 08:06:23 -08:00 committed by Aurelien Jarno
parent 79ef2c4cdb
commit 32188a03da
2 changed files with 44 additions and 24 deletions

View File

@ -369,6 +369,24 @@ static const char * const excp_names[EXCP_LAST + 1] = {
[EXCP_CACHE] = "cache error", [EXCP_CACHE] = "cache error",
}; };
#if !defined(CONFIG_USER_ONLY)
static target_ulong exception_resume_pc (CPUState *env)
{
target_ulong bad_pc;
target_ulong isa_mode;
isa_mode = !!(env->hflags & MIPS_HFLAG_M16);
bad_pc = env->active_tc.PC | isa_mode;
if (env->hflags & MIPS_HFLAG_BMASK) {
/* If the exception was raised from a delay slot, come back to
the jump. */
bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
}
return bad_pc;
}
#endif
void do_interrupt (CPUState *env) void do_interrupt (CPUState *env)
{ {
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
@ -396,7 +414,7 @@ void do_interrupt (CPUState *env)
resume will always occur on the next instruction resume will always occur on the next instruction
(but we assume the pc has always been updated during (but we assume the pc has always been updated during
code translation). */ code translation). */
env->CP0_DEPC = env->active_tc.PC; env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16);
goto enter_debug_mode; goto enter_debug_mode;
case EXCP_DINT: case EXCP_DINT:
env->CP0_Debug |= 1 << CP0DB_DINT; env->CP0_Debug |= 1 << CP0DB_DINT;
@ -413,14 +431,8 @@ void do_interrupt (CPUState *env)
case EXCP_DDBL: case EXCP_DDBL:
env->CP0_Debug |= 1 << CP0DB_DDBL; env->CP0_Debug |= 1 << CP0DB_DDBL;
set_DEPC: set_DEPC:
if (env->hflags & MIPS_HFLAG_BMASK) { env->CP0_DEPC = exception_resume_pc(env);
/* If the exception was raised from a delay slot,
come back to the jump. */
env->CP0_DEPC = env->active_tc.PC - 4;
env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags &= ~MIPS_HFLAG_BMASK;
} else {
env->CP0_DEPC = env->active_tc.PC;
}
enter_debug_mode: enter_debug_mode:
env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
env->hflags &= ~(MIPS_HFLAG_KSU); env->hflags &= ~(MIPS_HFLAG_KSU);
@ -428,6 +440,8 @@ void do_interrupt (CPUState *env)
if (!(env->CP0_Status & (1 << CP0St_EXL))) if (!(env->CP0_Status & (1 << CP0St_EXL)))
env->CP0_Cause &= ~(1 << CP0Ca_BD); env->CP0_Cause &= ~(1 << CP0Ca_BD);
env->active_tc.PC = (int32_t)0xBFC00480; env->active_tc.PC = (int32_t)0xBFC00480;
/* Exception handlers are entered in 32-bit mode. */
env->hflags &= ~(MIPS_HFLAG_M16);
break; break;
case EXCP_RESET: case EXCP_RESET:
cpu_reset(env); cpu_reset(env);
@ -439,20 +453,16 @@ void do_interrupt (CPUState *env)
case EXCP_NMI: case EXCP_NMI:
env->CP0_Status |= (1 << CP0St_NMI); env->CP0_Status |= (1 << CP0St_NMI);
set_error_EPC: set_error_EPC:
if (env->hflags & MIPS_HFLAG_BMASK) { env->CP0_ErrorEPC = exception_resume_pc(env);
/* If the exception was raised from a delay slot,
come back to the jump. */
env->CP0_ErrorEPC = env->active_tc.PC - 4;
env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags &= ~MIPS_HFLAG_BMASK;
} else {
env->CP0_ErrorEPC = env->active_tc.PC;
}
env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV);
env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
env->hflags &= ~(MIPS_HFLAG_KSU); env->hflags &= ~(MIPS_HFLAG_KSU);
if (!(env->CP0_Status & (1 << CP0St_EXL))) if (!(env->CP0_Status & (1 << CP0St_EXL)))
env->CP0_Cause &= ~(1 << CP0Ca_BD); env->CP0_Cause &= ~(1 << CP0Ca_BD);
env->active_tc.PC = (int32_t)0xBFC00000; env->active_tc.PC = (int32_t)0xBFC00000;
/* Exception handlers are entered in 32-bit mode. */
env->hflags &= ~(MIPS_HFLAG_M16);
break; break;
case EXCP_EXT_INTERRUPT: case EXCP_EXT_INTERRUPT:
cause = 0; cause = 0;
@ -554,13 +564,10 @@ void do_interrupt (CPUState *env)
} }
set_EPC: set_EPC:
if (!(env->CP0_Status & (1 << CP0St_EXL))) { if (!(env->CP0_Status & (1 << CP0St_EXL))) {
env->CP0_EPC = exception_resume_pc(env);
if (env->hflags & MIPS_HFLAG_BMASK) { if (env->hflags & MIPS_HFLAG_BMASK) {
/* If the exception was raised from a delay slot,
come back to the jump. */
env->CP0_EPC = env->active_tc.PC - 4;
env->CP0_Cause |= (1 << CP0Ca_BD); env->CP0_Cause |= (1 << CP0Ca_BD);
} else { } else {
env->CP0_EPC = env->active_tc.PC;
env->CP0_Cause &= ~(1 << CP0Ca_BD); env->CP0_Cause &= ~(1 << CP0Ca_BD);
} }
env->CP0_Status |= (1 << CP0St_EXL); env->CP0_Status |= (1 << CP0St_EXL);
@ -574,6 +581,8 @@ void do_interrupt (CPUState *env)
env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff); env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
} }
env->active_tc.PC += offset; env->active_tc.PC += offset;
/* Exception handlers are entered in 32-bit mode. */
env->hflags &= ~(MIPS_HFLAG_M16);
env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
break; break;
default: default:

View File

@ -1684,14 +1684,24 @@ static void debug_post_eret (void)
} }
} }
static void set_pc (target_ulong error_pc)
{
env->active_tc.PC = error_pc & ~(target_ulong)1;
if (error_pc & 1) {
env->hflags |= MIPS_HFLAG_M16;
} else {
env->hflags &= ~(MIPS_HFLAG_M16);
}
}
void helper_eret (void) void helper_eret (void)
{ {
debug_pre_eret(); debug_pre_eret();
if (env->CP0_Status & (1 << CP0St_ERL)) { if (env->CP0_Status & (1 << CP0St_ERL)) {
env->active_tc.PC = env->CP0_ErrorEPC; set_pc(env->CP0_ErrorEPC);
env->CP0_Status &= ~(1 << CP0St_ERL); env->CP0_Status &= ~(1 << CP0St_ERL);
} else { } else {
env->active_tc.PC = env->CP0_EPC; set_pc(env->CP0_EPC);
env->CP0_Status &= ~(1 << CP0St_EXL); env->CP0_Status &= ~(1 << CP0St_EXL);
} }
compute_hflags(env); compute_hflags(env);
@ -1702,7 +1712,8 @@ void helper_eret (void)
void helper_deret (void) void helper_deret (void)
{ {
debug_pre_eret(); debug_pre_eret();
env->active_tc.PC = env->CP0_DEPC; set_pc(env->CP0_DEPC);
env->hflags &= MIPS_HFLAG_DM; env->hflags &= MIPS_HFLAG_DM;
compute_hflags(env); compute_hflags(env);
debug_post_eret(); debug_post_eret();