diff options
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r-- | target-arm/translate.c | 54 |
1 files changed, 50 insertions, 4 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c index 164b52a0d0..ef62f8b0c4 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -28,6 +28,7 @@ #include "qemu/log.h" #include "qemu/bitops.h" #include "arm_ldst.h" +#include "exec/semihost.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -1144,6 +1145,33 @@ static inline void gen_lookup_tb(DisasContext *s) s->is_jmp = DISAS_JUMP; } +static inline void gen_hlt(DisasContext *s, int imm) +{ + /* HLT. This has two purposes. + * Architecturally, it is an external halting debug instruction. + * Since QEMU doesn't implement external debug, we treat this as + * it is required for halting debug disabled: it will UNDEF. + * Secondly, "HLT 0x3C" is a T32 semihosting trap instruction, + * and "HLT 0xF000" is an A32 semihosting syscall. These traps + * must trigger semihosting even for ARMv7 and earlier, where + * HLT was an undefined encoding. + * In system mode, we don't allow userspace access to + * semihosting, to provide some semblance of security + * (and for consistency with our 32-bit semihosting). + */ + if (semihosting_enabled() && +#ifndef CONFIG_USER_ONLY + s->current_el != 0 && +#endif + (imm == (s->thumb ? 0x3c : 0xf000))) { + gen_exception_internal_insn(s, 0, EXCP_SEMIHOST); + return; + } + + gen_exception_insn(s, s->thumb ? 2 : 4, EXCP_UDEF, syn_uncategorized(), + default_exception_el(s)); +} + static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, TCGv_i32 var) { @@ -8395,6 +8423,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); switch (op1) { + case 0: + /* HLT */ + gen_hlt(s, imm16); + break; case 1: /* bkpt */ ARCH(5); @@ -8419,7 +8451,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) gen_smc(s); break; default: - goto illegal_op; + g_assert_not_reached(); } break; } @@ -11451,19 +11483,33 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) break; } - case 0xa: /* rev */ + case 0xa: /* rev, and hlt */ + { + int op1 = extract32(insn, 6, 2); + + if (op1 == 2) { + /* HLT */ + int imm6 = extract32(insn, 0, 6); + + gen_hlt(s, imm6); + break; + } + + /* Otherwise this is rev */ ARCH(6); rn = (insn >> 3) & 0x7; rd = insn & 0x7; tmp = load_reg(s, rn); - switch ((insn >> 6) & 3) { + switch (op1) { case 0: tcg_gen_bswap32_i32(tmp, tmp); break; case 1: gen_rev16(tmp); break; case 3: gen_revsh(tmp); break; - default: goto illegal_op; + default: + g_assert_not_reached(); } store_reg(s, rd, tmp); break; + } case 6: switch ((insn >> 5) & 7) { |