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>
This commit is contained in:
Aurelien Jarno 2015-06-13 00:46:00 +02:00 committed by Alexander Graf
parent 8d302e7675
commit 311918b979
5 changed files with 100 additions and 3 deletions

View File

@ -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

View File

@ -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";

View File

@ -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);

View File

@ -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 */

View File

@ -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);
}