target-s390x: PER: add Breaking-Event-Address register

This patch adds support for PER Breaking-Event-Address register. Like
real hardware, it save the current PSW address when the PSW address is
changed by an instruction. We have to take care of optimizations QEMU
does, a branch to the next instruction is still a branch.

This register is copied to low core memory when a program exception
happens.

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:03 +02:00 committed by Alexander Graf
parent 83bb161299
commit 3da0ab3529
4 changed files with 37 additions and 11 deletions

View File

@ -117,6 +117,9 @@ static void s390_cpu_initial_reset(CPUState *s)
env->cregs[0] = CR0_RESET;
env->cregs[14] = CR14_RESET;
/* architectured initial value for Breaking-Event-Address register */
env->gbea = 1;
env->pfault_token = -1UL;
env->ext_index = -1;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {
@ -152,6 +155,9 @@ static void s390_cpu_full_reset(CPUState *s)
env->cregs[0] = CR0_RESET;
env->cregs[14] = CR14_RESET;
/* architectured initial value for Breaking-Event-Address register */
env->gbea = 1;
env->pfault_token = -1UL;
env->ext_index = -1;
for (i = 0; i < ARRAY_SIZE(env->io_index); i++) {

View File

@ -789,14 +789,16 @@ typedef struct LowCore
uint8_t pad5[0xf4-0xf0]; /* 0x0f0 */
uint32_t external_damage_code; /* 0x0f4 */
uint64_t failing_storage_address; /* 0x0f8 */
uint8_t pad6[0x120-0x100]; /* 0x100 */
uint8_t pad6[0x110-0x100]; /* 0x100 */
uint64_t per_breaking_event_addr; /* 0x110 */
uint8_t pad7[0x120-0x118]; /* 0x118 */
PSW restart_old_psw; /* 0x120 */
PSW external_old_psw; /* 0x130 */
PSW svc_old_psw; /* 0x140 */
PSW program_old_psw; /* 0x150 */
PSW mcck_old_psw; /* 0x160 */
PSW io_old_psw; /* 0x170 */
uint8_t pad7[0x1a0-0x180]; /* 0x180 */
uint8_t pad8[0x1a0-0x180]; /* 0x180 */
PSW restart_new_psw; /* 0x1a0 */
PSW external_new_psw; /* 0x1b0 */
PSW svc_new_psw; /* 0x1c0 */
@ -814,10 +816,10 @@ typedef struct LowCore
uint64_t last_update_clock; /* 0x280 */
uint64_t steal_clock; /* 0x288 */
PSW return_mcck_psw; /* 0x290 */
uint8_t pad8[0xc00-0x2a0]; /* 0x2a0 */
uint8_t pad9[0xc00-0x2a0]; /* 0x2a0 */
/* System info area */
uint64_t save_area[16]; /* 0xc00 */
uint8_t pad9[0xd40-0xc80]; /* 0xc80 */
uint8_t pad10[0xd40-0xc80]; /* 0xc80 */
uint64_t kernel_stack; /* 0xd40 */
uint64_t thread_info; /* 0xd48 */
uint64_t async_stack; /* 0xd50 */
@ -825,7 +827,7 @@ typedef struct LowCore
uint64_t user_asce; /* 0xd60 */
uint64_t panic_stack; /* 0xd68 */
uint64_t user_exec_asce; /* 0xd70 */
uint8_t pad10[0xdc0-0xd78]; /* 0xd78 */
uint8_t pad11[0xdc0-0xd78]; /* 0xd78 */
/* SMP info area: defined by DJB */
uint64_t clock_comparator; /* 0xdc0 */

View File

@ -293,6 +293,7 @@ static void do_program_interrupt(CPUS390XState *env)
lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
mask = be64_to_cpu(lowcore->program_new_psw.mask);
addr = be64_to_cpu(lowcore->program_new_psw.addr);
lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea);
cpu_unmap_lowcore(lowcore);

View File

@ -150,6 +150,7 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
static TCGv_i64 psw_addr;
static TCGv_i64 psw_mask;
static TCGv_i64 gbea;
static TCGv_i32 cc_op;
static TCGv_i64 cc_src;
@ -173,6 +174,9 @@ void s390x_translate_init(void)
psw_mask = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUS390XState, psw.mask),
"psw_mask");
gbea = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUS390XState, gbea),
"gbea");
cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUS390XState, cc_op),
"cc_op");
@ -252,14 +256,14 @@ static void update_psw_addr(DisasContext *s)
static void per_branch(DisasContext *s, bool to_next)
{
#ifndef CONFIG_USER_ONLY
tcg_gen_movi_i64(gbea, s->pc);
if (s->tb->flags & FLAG_MASK_PER) {
TCGv_i64 pc = tcg_const_i64(s->pc);
TCGv_i64 next_pc = to_next ? tcg_const_i64(s->next_pc) : psw_addr;
gen_helper_per_branch(cpu_env, pc, next_pc);
gen_helper_per_branch(cpu_env, gbea, next_pc);
if (to_next) {
tcg_temp_free_i64(next_pc);
}
tcg_temp_free_i64(pc);
}
#endif
}
@ -272,15 +276,23 @@ static void per_branch_cond(DisasContext *s, TCGCond cond,
TCGLabel *lab = gen_new_label();
tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab);
TCGv_i64 pc = tcg_const_i64(s->pc);
gen_helper_per_branch(cpu_env, pc, psw_addr);
tcg_temp_free_i64(pc);
tcg_gen_movi_i64(gbea, s->pc);
gen_helper_per_branch(cpu_env, gbea, psw_addr);
gen_set_label(lab);
} else {
TCGv_i64 pc = tcg_const_i64(s->pc);
tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc);
tcg_temp_free_i64(pc);
}
#endif
}
static void per_breaking_event(DisasContext *s)
{
tcg_gen_movi_i64(gbea, s->pc);
}
static void update_cc_op(DisasContext *s)
{
if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) {
@ -1220,6 +1232,7 @@ static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest)
}
if (use_goto_tb(s, dest)) {
update_cc_op(s);
per_breaking_event(s);
tcg_gen_goto_tb(0);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb);
@ -1287,6 +1300,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
/* Branch taken. */
gen_set_label(lab);
per_breaking_event(s);
tcg_gen_goto_tb(1);
tcg_gen_movi_i64(psw_addr, dest);
tcg_gen_exit_tb((uintptr_t)s->tb + 1);
@ -1318,6 +1332,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c,
if (is_imm) {
tcg_gen_movi_i64(psw_addr, dest);
}
per_breaking_event(s);
ret = EXIT_PC_UPDATED;
}
} else {
@ -2550,6 +2565,7 @@ static ExitStatus op_lpsw(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
@ -2569,6 +2585,7 @@ static ExitStatus op_lpswe(DisasContext *s, DisasOps *o)
TCGv_i64 t1, t2;
check_privileged(s);
per_breaking_event(s);
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();