aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAurelien Jarno <aurelien@aurel32.net>2015-06-13 00:46:00 +0200
committerAlexander Graf <agraf@suse.de>2015-06-17 12:40:52 +0200
commit311918b979c5364c30392c1054ed77d047a83953 (patch)
tree3ca255bed7666787a2bf045a26a696b4dab97b67
parent8d302e76755b8157373073d7107e31b0b13f80c1 (diff)
target-s390x: PER storage-alteration event support
For the PER storage-alteration event we can use the QEMU watchpoint infrastructure. When PER is enabled or PER control register changed we enable the corresponding watchpoints. When a watchpoint arises we can save the event. Unfortunately the current code does not provide the address space used to trigger the watchpoint. For now we assume it comes from the default ASC. Signed-off-by: Aurelien Jarno <aurelien@aurel32.net> Signed-off-by: Alexander Graf <agraf@suse.de>
-rw-r--r--target-s390x/cpu-qom.h1
-rw-r--r--target-s390x/cpu.c1
-rw-r--r--target-s390x/cpu.h1
-rw-r--r--target-s390x/helper.c75
-rw-r--r--target-s390x/mem_helper.c25
5 files changed, 100 insertions, 3 deletions
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);
}