diff options
-rw-r--r-- | linux-user/main.c | 56 | ||||
-rw-r--r-- | target-arm/cpu.h | 15 | ||||
-rw-r--r-- | target-arm/helper.c | 23 | ||||
-rw-r--r-- | target-arm/machine.c | 3 |
4 files changed, 56 insertions, 41 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index af924dc..947358a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -483,17 +483,17 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) addr = env->regs[2]; if (get_user_u64(oldval, env->regs[0])) { - env->cp15.c6_data = env->regs[0]; + env->exception.vaddress = env->regs[0]; goto segv; }; if (get_user_u64(newval, env->regs[1])) { - env->cp15.c6_data = env->regs[1]; + env->exception.vaddress = env->regs[1]; goto segv; }; if (get_user_u64(val, addr)) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto segv; } @@ -501,7 +501,7 @@ static void arm_kernel_cmpxchg64_helper(CPUARMState *env) val = newval; if (put_user_u64(val, addr)) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto segv; }; @@ -523,7 +523,7 @@ segv: info.si_errno = 0; /* XXX: check env->error_code */ info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->cp15.c6_data; + info._sifields._sigfault._addr = env->exception.vaddress; queue_signal(env, info.si_signo, &info); end_exclusive(); @@ -620,14 +620,14 @@ static int do_strex(CPUARMState *env) abort(); } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto done; } if (size == 3) { uint32_t valhi; segv = get_user_u32(valhi, addr + 4); if (segv) { - env->cp15.c6_data = addr + 4; + env->exception.vaddress = addr + 4; goto done; } val = deposit64(val, 32, 32, valhi); @@ -650,14 +650,14 @@ static int do_strex(CPUARMState *env) break; } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto done; } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; segv = put_user_u32(val, addr + 4); if (segv) { - env->cp15.c6_data = addr + 4; + env->exception.vaddress = addr + 4; goto done; } } @@ -832,12 +832,14 @@ void cpu_loop(CPUARMState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; + case EXCP_STREX: + if (!do_strex(env)) { + break; + } + /* fall through for segv */ case EXCP_PREFETCH_ABORT: - addr = env->cp15.c6_insn; - goto do_segv; case EXCP_DATA_ABORT: - addr = env->cp15.c6_data; - do_segv: + addr = env->exception.vaddress; { info.si_signo = SIGSEGV; info.si_errno = 0; @@ -865,12 +867,6 @@ void cpu_loop(CPUARMState *env) if (do_kernel_trap(env)) goto error; break; - case EXCP_STREX: - if (do_strex(env)) { - addr = env->cp15.c6_data; - goto do_segv; - } - break; default: error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", @@ -933,7 +929,7 @@ static int do_strex_a64(CPUARMState *env) abort(); } if (segv) { - env->cp15.c6_data = addr; + env->exception.vaddress = addr; goto error; } if (val != env->exclusive_val) { @@ -946,7 +942,7 @@ static int do_strex_a64(CPUARMState *env) segv = get_user_u64(val, addr + 8); } if (segv) { - env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + env->exception.vaddress = addr + (size == 2 ? 4 : 8); goto error; } if (val != env->exclusive_high) { @@ -981,7 +977,7 @@ static int do_strex_a64(CPUARMState *env) segv = put_user_u64(val, addr + 8); } if (segv) { - env->cp15.c6_data = addr + (size == 2 ? 4 : 8); + env->exception.vaddress = addr + (size == 2 ? 4 : 8); goto error; } } @@ -1037,12 +1033,14 @@ void cpu_loop(CPUARMState *env) info._sifields._sigfault._addr = env->pc; queue_signal(env, info.si_signo, &info); break; + case EXCP_STREX: + if (!do_strex_a64(env)) { + break; + } + /* fall through for segv */ case EXCP_PREFETCH_ABORT: - addr = env->cp15.c6_insn; - goto do_segv; case EXCP_DATA_ABORT: - addr = env->cp15.c6_data; - do_segv: + addr = env->exception.vaddress; info.si_signo = SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ @@ -1060,12 +1058,6 @@ void cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); } break; - case EXCP_STREX: - if (do_strex_a64(env)) { - addr = env->cp15.c6_data; - goto do_segv; - } - break; default: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 695a1e2..7a48241 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -233,6 +233,21 @@ typedef struct CPUARMState { int pending_exception; } v7m; + /* Information associated with an exception about to be taken: + * code which raises an exception must set cs->exception_index and + * the relevant parts of this structure; the cpu_do_interrupt function + * will then set the guest-visible registers as part of the exception + * entry process. + */ + struct { + uint32_t syndrome; /* AArch64 format syndrome register */ + uint32_t fsr; /* AArch32 format fault status register info */ + uint64_t vaddress; /* virtual addr associated with exception, if any */ + /* If we implement EL2 we will also need to store information + * about the intermediate physical address for stage 2 faults. + */ + } exception; + /* Thumb-2 EE state. */ uint32_t teecr; uint32_t teehbr; diff --git a/target-arm/helper.c b/target-arm/helper.c index c8348da..fe642df 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2705,12 +2705,11 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + env->exception.vaddress = address; if (rw == 2) { cs->exception_index = EXCP_PREFETCH_ABORT; - env->cp15.c6_insn = address; } else { cs->exception_index = EXCP_DATA_ABORT; - env->cp15.c6_data = address; } return 1; } @@ -2928,6 +2927,9 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) return; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: + /* TODO: if we implemented the MPU registers, this is where we + * should set the MMFAR, etc from exception.fsr and exception.vaddress. + */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); return; case EXCP_BKPT: @@ -3042,9 +3044,11 @@ void arm_cpu_do_interrupt(CPUState *cs) return; } } - env->cp15.c5_insn = 2; + env->exception.fsr = 2; /* Fall through to prefetch abort. */ case EXCP_PREFETCH_ABORT: + env->cp15.c5_insn = env->exception.fsr; + env->cp15.c6_insn = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, "...with IFSR 0x%x IFAR 0x%x\n", env->cp15.c5_insn, env->cp15.c6_insn); new_mode = ARM_CPU_MODE_ABT; @@ -3053,6 +3057,8 @@ void arm_cpu_do_interrupt(CPUState *cs) offset = 4; break; case EXCP_DATA_ABORT: + env->cp15.c5_data = env->exception.fsr; + env->cp15.c6_data = env->exception.vaddress; qemu_log_mask(CPU_LOG_INT, "...with DFSR 0x%x DFAR 0x%x\n", env->cp15.c5_data, env->cp15.c6_data); new_mode = ARM_CPU_MODE_ABT; @@ -3703,16 +3709,15 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, } if (access_type == 2) { - env->cp15.c5_insn = ret; - env->cp15.c6_insn = address; cs->exception_index = EXCP_PREFETCH_ABORT; } else { - env->cp15.c5_data = ret; - if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6)) - env->cp15.c5_data |= (1 << 11); - env->cp15.c6_data = address; + if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6)) { + ret |= (1 << 11); + } cs->exception_index = EXCP_DATA_ABORT; } + env->exception.vaddress = address; + env->exception.fsr = ret; return 1; } diff --git a/target-arm/machine.c b/target-arm/machine.c index 7ced87a..b9d9c0f 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -257,6 +257,9 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_UINT64(env.exclusive_val, ARMCPU), VMSTATE_UINT64(env.exclusive_high, ARMCPU), VMSTATE_UINT64(env.features, ARMCPU), + VMSTATE_UINT32(env.exception.syndrome, ARMCPU), + VMSTATE_UINT32(env.exception.fsr, ARMCPU), + VMSTATE_UINT64(env.exception.vaddress, ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), VMSTATE_END_OF_LIST() |