diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h index 936ae21e06..491c1b8769 100644 --- a/target-s390x/cpu-qom.h +++ b/target-s390x/cpu-qom.h @@ -98,5 +98,6 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void s390_cpu_gdb_init(CPUState *cs); +void s390x_cpu_debug_excp_handler(CPUState *cs); #endif diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index ba7a887eea..4daf6439f5 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -343,6 +343,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) cc->write_elf64_note = s390_cpu_write_elf64_note; cc->write_elf64_qemunote = s390_cpu_write_elf64_qemunote; cc->cpu_exec_interrupt = s390_cpu_exec_interrupt; + cc->debug_excp_handler = s390x_cpu_debug_excp_handler; #endif cc->gdb_num_core_regs = S390_NUM_CORE_REGS; cc->gdb_core_xml_file = "s390x-core64.xml"; diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index f830208d25..68321f57b8 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -1045,6 +1045,7 @@ int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc, int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code); uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr); +void s390_cpu_recompute_watchpoints(CPUState *cs); int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, int len, bool is_write); diff --git a/target-s390x/helper.c b/target-s390x/helper.c index ec847a2645..615cccf53f 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -181,12 +181,18 @@ hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr) void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { + uint64_t old_mask = env->psw.mask; + env->psw.addr = addr; env->psw.mask = mask; if (tcg_enabled()) { env->cc_op = (mask >> 44) & 3; } + if ((old_mask ^ mask) & PSW_MASK_PER) { + s390_cpu_recompute_watchpoints(CPU(s390_env_get_cpu(env))); + } + if (mask & PSW_MASK_WAIT) { S390CPU *cpu = s390_env_get_cpu(env); if (s390_cpu_halt(cpu) == 0) { @@ -573,4 +579,73 @@ bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } + +void s390_cpu_recompute_watchpoints(CPUState *cs) +{ + const int wp_flags = BP_CPU | BP_MEM_WRITE | BP_STOP_BEFORE_ACCESS; + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + /* We are called when the watchpoints have changed. First + remove them all. */ + cpu_watchpoint_remove_all(cs, BP_CPU); + + /* Return if PER is not enabled */ + if (!(env->psw.mask & PSW_MASK_PER)) { + return; + } + + /* Return if storage-alteration event is not enabled. */ + if (!(env->cregs[9] & PER_CR9_EVENT_STORE)) { + return; + } + + if (env->cregs[10] == 0 && env->cregs[11] == -1LL) { + /* We can't create a watchoint spanning the whole memory range, so + split it in two parts. */ + cpu_watchpoint_insert(cs, 0, 1ULL << 63, wp_flags, NULL); + cpu_watchpoint_insert(cs, 1ULL << 63, 1ULL << 63, wp_flags, NULL); + } else if (env->cregs[10] > env->cregs[11]) { + /* The address range loops, create two watchpoints. */ + cpu_watchpoint_insert(cs, env->cregs[10], -env->cregs[10], + wp_flags, NULL); + cpu_watchpoint_insert(cs, 0, env->cregs[11] + 1, wp_flags, NULL); + + } else { + /* Default case, create a single watchpoint. */ + cpu_watchpoint_insert(cs, env->cregs[10], + env->cregs[11] - env->cregs[10] + 1, + wp_flags, NULL); + } +} + +void s390x_cpu_debug_excp_handler(CPUState *cs) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + CPUWatchpoint *wp_hit = cs->watchpoint_hit; + + if (wp_hit && wp_hit->flags & BP_CPU) { + /* FIXME: When the storage-alteration-space control bit is set, + the exception should only be triggered if the memory access + is done using an address space with the storage-alteration-event + bit set. We have no way to detect that with the current + watchpoint code. */ + cs->watchpoint_hit = NULL; + + env->per_address = env->psw.addr; + env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env); + /* FIXME: We currently no way to detect the address space used + to trigger the watchpoint. For now just consider it is the + current default ASC. This turn to be true except when MVCP + and MVCS instrutions are not used. */ + env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46; + + /* Remove all watchpoints to re-execute the code. A PER exception + will be triggered, it will call load_psw which will recompute + the watchpoints. */ + cpu_watchpoint_remove_all(cs, BP_CPU); + cpu_resume_from_signal(cs, NULL); + } +} #endif /* CONFIG_USER_ONLY */ diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index 6427ee9b6b..d03f9fd2f3 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -841,11 +841,17 @@ uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) { S390CPU *cpu = s390_env_get_cpu(env); + bool PERchanged = false; int i; uint64_t src = a2; + uint64_t val; for (i = r1;; i = (i + 1) % 16) { - env->cregs[i] = cpu_ldq_data(env, src); + val = cpu_ldq_data(env, src); + if (env->cregs[i] != val && i >= 9 && i <= 11) { + PERchanged = true; + } + env->cregs[i] = val; HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n", i, src, env->cregs[i]); src += sizeof(uint64_t); @@ -855,18 +861,27 @@ void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) } } + if (PERchanged && env->psw.mask & PSW_MASK_PER) { + s390_cpu_recompute_watchpoints(CPU(cpu)); + } + tlb_flush(CPU(cpu), 1); } void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) { S390CPU *cpu = s390_env_get_cpu(env); + bool PERchanged = false; int i; uint64_t src = a2; + uint32_t val; for (i = r1;; i = (i + 1) % 16) { - env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | - cpu_ldl_data(env, src); + val = cpu_ldl_data(env, src); + if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) { + PERchanged = true; + } + env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val; src += sizeof(uint32_t); if (i == r3) { @@ -874,6 +889,10 @@ void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) } } + if (PERchanged && env->psw.mask & PSW_MASK_PER) { + s390_cpu_recompute_watchpoints(CPU(cpu)); + } + tlb_flush(CPU(cpu), 1); }