diff options
Diffstat (limited to 'target/arm/translate.c')
| -rw-r--r-- | target/arm/translate.c | 76 |
1 files changed, 57 insertions, 19 deletions
diff --git a/target/arm/translate.c b/target/arm/translate.c index f845da7c63..bcfc29c5a6 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -4506,10 +4506,14 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, } break; case ARM_CPU_MODE_HYP: - /* Note that we can forbid accesses from EL2 here because they - * must be from Hyp mode itself + /* + * SPSR_hyp and r13_hyp can only be accessed from Monitor mode + * (and so we can forbid accesses from EL2 or below). elr_hyp + * can be accessed also from Hyp mode, so forbid accesses from + * EL0 or EL1. */ - if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) { + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 || + (s->current_el < 3 && *regno != 17)) { goto undef; } break; @@ -8480,6 +8484,22 @@ static void gen_srs(DisasContext *s, s->base.is_jmp = DISAS_UPDATE; } +/* Generate a label used for skipping this instruction */ +static void arm_gen_condlabel(DisasContext *s) +{ + if (!s->condjmp) { + s->condlabel = gen_new_label(); + s->condjmp = 1; + } +} + +/* Skip this instruction if the ARM condition is false */ +static void arm_skip_unless(DisasContext *s, uint32_t cond) +{ + arm_gen_condlabel(s); + arm_gen_test_cc(cond ^ 1, s->condlabel); +} + static void disas_arm_insn(DisasContext *s, unsigned int insn) { unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh; @@ -8709,9 +8729,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (cond != 0xe) { /* if not always execute, we generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); } if ((insn & 0x0f900000) == 0x03000000) { if ((insn & (1 << 21)) == 0) { @@ -8883,6 +8901,25 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; + case 0x6: /* ERET */ + if (op1 != 3) { + goto illegal_op; + } + if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) { + goto illegal_op; + } + if ((insn & 0x000fff0f) != 0x0000000e) { + /* UNPREDICTABLE; we choose to UNDEF */ + goto illegal_op; + } + + if (s->current_el == 2) { + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, 14); + } + gen_exception_return(s, tmp); + break; case 7: { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); @@ -11140,8 +11177,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (rn != 14 || rd != 15) { goto illegal_op; } - tmp = load_reg(s, rn); - tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + if (s->current_el == 2) { + /* ERET from Hyp uses ELR_Hyp, not LR */ + if (insn & 0xff) { + goto illegal_op; + } + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, rn); + tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + } gen_exception_return(s, tmp); break; case 6: /* MRS */ @@ -11205,9 +11250,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) /* Conditional branch. */ op = (insn >> 22) & 0xf; /* Generate a conditional jump to next instruction. */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(op ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, op); /* offset[11:1] = insn[10:0] */ offset = (insn & 0x7ff) << 1; @@ -12131,8 +12174,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) case 1: case 3: case 9: case 11: /* czb */ rm = insn & 7; tmp = load_reg(s, rm); - s->condlabel = gen_new_label(); - s->condjmp = 1; + arm_gen_condlabel(s); if (insn & (1 << 11)) tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel); else @@ -12295,9 +12337,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; } /* generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); /* jump to the offset */ val = (uint32_t)s->pc + 2; @@ -12676,9 +12716,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) uint32_t cond = dc->condexec_cond; if (cond != 0x0e) { /* Skip conditional when condition is AL. */ - dc->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, dc->condlabel); - dc->condjmp = 1; + arm_skip_unless(dc, cond); } } |