target-mips: Correct the writes to Status and Cause registers via gdbstub
Make writes to CP0.Status and CP0.Cause have the same effect as executing corresponding MTC0 instructions would in Kernel Mode. Also ignore writes in the user emulation mode. Currently for requests from the GDB stub we write all the bits across both registers, ignoring any read-only locations, and do not synchronise the environment to evaluate side effects. We also write these registers in the user emulation mode even though a real kernel presents them as read only. Signed-off-by: Maciej W. Rozycki <macro@codesourcery.com> Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
This commit is contained in:
parent
f88f79ec9d
commit
81a423e6c6
@ -904,4 +904,93 @@ static inline void compute_hflags(CPUMIPSState *env)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/* Called for updates to CP0_Status. */
|
||||||
|
static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
|
||||||
|
{
|
||||||
|
int32_t tcstatus, *tcst;
|
||||||
|
uint32_t v = cpu->CP0_Status;
|
||||||
|
uint32_t cu, mx, asid, ksu;
|
||||||
|
uint32_t mask = ((1 << CP0TCSt_TCU3)
|
||||||
|
| (1 << CP0TCSt_TCU2)
|
||||||
|
| (1 << CP0TCSt_TCU1)
|
||||||
|
| (1 << CP0TCSt_TCU0)
|
||||||
|
| (1 << CP0TCSt_TMX)
|
||||||
|
| (3 << CP0TCSt_TKSU)
|
||||||
|
| (0xff << CP0TCSt_TASID));
|
||||||
|
|
||||||
|
cu = (v >> CP0St_CU0) & 0xf;
|
||||||
|
mx = (v >> CP0St_MX) & 0x1;
|
||||||
|
ksu = (v >> CP0St_KSU) & 0x3;
|
||||||
|
asid = env->CP0_EntryHi & 0xff;
|
||||||
|
|
||||||
|
tcstatus = cu << CP0TCSt_TCU0;
|
||||||
|
tcstatus |= mx << CP0TCSt_TMX;
|
||||||
|
tcstatus |= ksu << CP0TCSt_TKSU;
|
||||||
|
tcstatus |= asid;
|
||||||
|
|
||||||
|
if (tc == cpu->current_tc) {
|
||||||
|
tcst = &cpu->active_tc.CP0_TCStatus;
|
||||||
|
} else {
|
||||||
|
tcst = &cpu->tcs[tc].CP0_TCStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
*tcst &= ~mask;
|
||||||
|
*tcst |= tcstatus;
|
||||||
|
compute_hflags(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cpu_mips_store_status(CPUMIPSState *env, target_ulong val)
|
||||||
|
{
|
||||||
|
uint32_t mask = env->CP0_Status_rw_bitmask;
|
||||||
|
|
||||||
|
if (env->insn_flags & ISA_MIPS32R6) {
|
||||||
|
bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3;
|
||||||
|
|
||||||
|
if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) {
|
||||||
|
mask &= ~(3 << CP0St_KSU);
|
||||||
|
}
|
||||||
|
mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CP0_Status = (env->CP0_Status & ~mask) | (val & mask);
|
||||||
|
if (env->CP0_Config3 & (1 << CP0C3_MT)) {
|
||||||
|
sync_c0_status(env, env, env->current_tc);
|
||||||
|
} else {
|
||||||
|
compute_hflags(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val)
|
||||||
|
{
|
||||||
|
uint32_t mask = 0x00C00300;
|
||||||
|
uint32_t old = env->CP0_Cause;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (env->insn_flags & ISA_MIPS32R2) {
|
||||||
|
mask |= 1 << CP0Ca_DC;
|
||||||
|
}
|
||||||
|
if (env->insn_flags & ISA_MIPS32R6) {
|
||||||
|
mask &= ~((1 << CP0Ca_WP) & val);
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask);
|
||||||
|
|
||||||
|
if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) {
|
||||||
|
if (env->CP0_Cause & (1 << CP0Ca_DC)) {
|
||||||
|
cpu_mips_stop_count(env);
|
||||||
|
} else {
|
||||||
|
cpu_mips_start_count(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set/reset software interrupts */
|
||||||
|
for (i = 0 ; i < 2 ; i++) {
|
||||||
|
if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
|
||||||
|
cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* !defined (__MIPS_CPU_H__) */
|
#endif /* !defined (__MIPS_CPU_H__) */
|
||||||
|
@ -112,7 +112,9 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
|||||||
}
|
}
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 32:
|
case 32:
|
||||||
env->CP0_Status = tmp;
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
cpu_mips_store_status(env, tmp);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 33:
|
case 33:
|
||||||
env->active_tc.LO[0] = tmp;
|
env->active_tc.LO[0] = tmp;
|
||||||
@ -124,7 +126,9 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
|||||||
env->CP0_BadVAddr = tmp;
|
env->CP0_BadVAddr = tmp;
|
||||||
break;
|
break;
|
||||||
case 36:
|
case 36:
|
||||||
env->CP0_Cause = tmp;
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
cpu_mips_store_cause(env, tmp);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 37:
|
case 37:
|
||||||
env->active_tc.PC = tmp & ~(target_ulong)1;
|
env->active_tc.PC = tmp & ~(target_ulong)1;
|
||||||
|
@ -625,40 +625,9 @@ static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc)
|
|||||||
|
|
||||||
These helper call synchronizes the regs for a given cpu. */
|
These helper call synchronizes the regs for a given cpu. */
|
||||||
|
|
||||||
/* Called for updates to CP0_Status. */
|
/* Called for updates to CP0_Status. Defined in "cpu.h" for gdbstub.c. */
|
||||||
static void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc)
|
/* static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu,
|
||||||
{
|
int tc); */
|
||||||
int32_t tcstatus, *tcst;
|
|
||||||
uint32_t v = cpu->CP0_Status;
|
|
||||||
uint32_t cu, mx, asid, ksu;
|
|
||||||
uint32_t mask = ((1 << CP0TCSt_TCU3)
|
|
||||||
| (1 << CP0TCSt_TCU2)
|
|
||||||
| (1 << CP0TCSt_TCU1)
|
|
||||||
| (1 << CP0TCSt_TCU0)
|
|
||||||
| (1 << CP0TCSt_TMX)
|
|
||||||
| (3 << CP0TCSt_TKSU)
|
|
||||||
| (0xff << CP0TCSt_TASID));
|
|
||||||
|
|
||||||
cu = (v >> CP0St_CU0) & 0xf;
|
|
||||||
mx = (v >> CP0St_MX) & 0x1;
|
|
||||||
ksu = (v >> CP0St_KSU) & 0x3;
|
|
||||||
asid = env->CP0_EntryHi & 0xff;
|
|
||||||
|
|
||||||
tcstatus = cu << CP0TCSt_TCU0;
|
|
||||||
tcstatus |= mx << CP0TCSt_TMX;
|
|
||||||
tcstatus |= ksu << CP0TCSt_TKSU;
|
|
||||||
tcstatus |= asid;
|
|
||||||
|
|
||||||
if (tc == cpu->current_tc) {
|
|
||||||
tcst = &cpu->active_tc.CP0_TCStatus;
|
|
||||||
} else {
|
|
||||||
tcst = &cpu->tcs[tc].CP0_TCStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
*tcst &= ~mask;
|
|
||||||
*tcst |= tcstatus;
|
|
||||||
compute_hflags(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called for updates to CP0_TCStatus. */
|
/* Called for updates to CP0_TCStatus. */
|
||||||
static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc,
|
static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc,
|
||||||
@ -1420,25 +1389,10 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1)
|
|||||||
{
|
{
|
||||||
MIPSCPU *cpu = mips_env_get_cpu(env);
|
MIPSCPU *cpu = mips_env_get_cpu(env);
|
||||||
uint32_t val, old;
|
uint32_t val, old;
|
||||||
uint32_t mask = env->CP0_Status_rw_bitmask;
|
|
||||||
|
|
||||||
if (env->insn_flags & ISA_MIPS32R6) {
|
|
||||||
bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3;
|
|
||||||
|
|
||||||
if (has_supervisor && extract32(arg1, CP0St_KSU, 2) == 0x3) {
|
|
||||||
mask &= ~(3 << CP0St_KSU);
|
|
||||||
}
|
|
||||||
mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
val = arg1 & mask;
|
|
||||||
old = env->CP0_Status;
|
old = env->CP0_Status;
|
||||||
env->CP0_Status = (env->CP0_Status & ~mask) | val;
|
cpu_mips_store_status(env, arg1);
|
||||||
if (env->CP0_Config3 & (1 << CP0C3_MT)) {
|
val = env->CP0_Status;
|
||||||
sync_c0_status(env, env, env->current_tc);
|
|
||||||
} else {
|
|
||||||
compute_hflags(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
|
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
|
||||||
qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x",
|
qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x",
|
||||||
@ -1477,40 +1431,9 @@ void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1)
|
|||||||
env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask);
|
env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtc0_cause(CPUMIPSState *cpu, target_ulong arg1)
|
|
||||||
{
|
|
||||||
uint32_t mask = 0x00C00300;
|
|
||||||
uint32_t old = cpu->CP0_Cause;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (cpu->insn_flags & ISA_MIPS32R2) {
|
|
||||||
mask |= 1 << CP0Ca_DC;
|
|
||||||
}
|
|
||||||
if (cpu->insn_flags & ISA_MIPS32R6) {
|
|
||||||
mask &= ~((1 << CP0Ca_WP) & arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu->CP0_Cause = (cpu->CP0_Cause & ~mask) | (arg1 & mask);
|
|
||||||
|
|
||||||
if ((old ^ cpu->CP0_Cause) & (1 << CP0Ca_DC)) {
|
|
||||||
if (cpu->CP0_Cause & (1 << CP0Ca_DC)) {
|
|
||||||
cpu_mips_stop_count(cpu);
|
|
||||||
} else {
|
|
||||||
cpu_mips_start_count(cpu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set/reset software interrupts */
|
|
||||||
for (i = 0 ; i < 2 ; i++) {
|
|
||||||
if ((old ^ cpu->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
|
|
||||||
cpu_mips_soft_irq(cpu, i, cpu->CP0_Cause & (1 << (CP0Ca_IP + i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1)
|
void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1)
|
||||||
{
|
{
|
||||||
mtc0_cause(env, arg1);
|
cpu_mips_store_cause(env, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1)
|
void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1)
|
||||||
@ -1518,7 +1441,7 @@ void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1)
|
|||||||
int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
|
int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
|
||||||
CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
|
CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
|
||||||
|
|
||||||
mtc0_cause(other, arg1);
|
cpu_mips_store_cause(other, arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ulong helper_mftc0_epc(CPUMIPSState *env)
|
target_ulong helper_mftc0_epc(CPUMIPSState *env)
|
||||||
|
Loading…
Reference in New Issue
Block a user