diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index b10a8541ff..40fdeaa905 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -41,6 +41,26 @@ #define CR0_RESET 0xE0UL #define CR14_RESET 0xC2000000UL; +#ifndef CONFIG_USER_ONLY +static bool is_early_exception_psw(uint64_t mask, uint64_t addr) +{ + if (mask & PSW_MASK_RESERVED) { + return true; + } + + switch (mask & (PSW_MASK_32 | PSW_MASK_64)) { + case 0: + return addr & ~0xffffffULL; + case PSW_MASK_32: + return addr & ~0x7fffffffULL; + case PSW_MASK_32 | PSW_MASK_64: + return false; + default: /* PSW_MASK_64 */ + return true; + } +} +#endif + void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { #ifndef CONFIG_USER_ONLY @@ -57,6 +77,12 @@ void s390_cpu_set_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) env->cc_op = (mask >> 44) & 3; #ifndef CONFIG_USER_ONLY + if (is_early_exception_psw(mask, addr)) { + env->int_pgm_ilen = 0; + trigger_pgm_exception(env, PGM_SPECIFICATION); + return; + } + if ((old_mask ^ mask) & PSW_MASK_PER) { s390_cpu_recompute_watchpoints(env_cpu(env)); } diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 7d6d01325b..16f6354751 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -292,6 +292,7 @@ extern const VMStateDescription vmstate_s390_cpu; #define PSW_MASK_32 0x0000000080000000ULL #define PSW_MASK_SHORT_ADDR 0x000000007fffffffULL #define PSW_MASK_SHORT_CTRL 0xffffffff80000000ULL +#define PSW_MASK_RESERVED 0xb80800fe7fffffffULL #undef PSW_ASC_PRIMARY #undef PSW_ASC_ACCREG diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index bc767f0443..a7829b1e49 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -212,7 +212,8 @@ static void do_program_interrupt(CPUS390XState *env) LowCore *lowcore; int ilen = env->int_pgm_ilen; - assert(ilen == 2 || ilen == 4 || ilen == 6); + assert((env->int_pgm_code == PGM_SPECIFICATION && ilen == 0) || + ilen == 2 || ilen == 4 || ilen == 6); switch (env->int_pgm_code) { case PGM_PER: diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 2e1e7e046a..7832cf02a6 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -4068,9 +4068,23 @@ static DisasJumpType op_sske(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static void gen_check_psw_mask(DisasContext *s) +{ + TCGv_i64 reserved = tcg_temp_new_i64(); + TCGLabel *ok = gen_new_label(); + + tcg_gen_andi_i64(reserved, psw_mask, PSW_MASK_RESERVED); + tcg_gen_brcondi_i64(TCG_COND_EQ, reserved, 0, ok); + gen_program_exception(s, PGM_SPECIFICATION); + gen_set_label(ok); +} + static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); + + gen_check_psw_mask(s); + /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY; @@ -4331,6 +4345,8 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) tcg_gen_ori_i64(psw_mask, psw_mask, i2 << 56); } + gen_check_psw_mask(s); + /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY;