aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-10-24 16:26:56 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-10-24 16:26:56 +0100
commit19a6e31c9d2701ef648b70ddcfc3bf64cec8c37e (patch)
treebe8919d03be424ede566db09efe884a98fa13c30
parentcaae8032d3bd0a54f09abdae7624f52dece5ffd6 (diff)
downloadqemu-arm-19a6e31c9d2701ef648b70ddcfc3bf64cec8c37e.tar.gz
target-arm: Implement new HLT trap for semihosting
Version 2.0 of the semihosting specification introduces new trap instructions for AArch32: HLT 0xF000 for A32 and HLT 0x3C for T32. Implement these (in the same way we implement the existing HLT semihosting trap for A64). The old traps via SVC and BKPT are unaffected. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1476792973-18508-1-git-send-email-peter.maydell@linaro.org
-rw-r--r--linux-user/main.c3
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/helper.c11
-rw-r--r--target-arm/translate.c54
4 files changed, 63 insertions, 7 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index c6f2e20c09..54970bc4d9 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -806,6 +806,9 @@ void cpu_loop(CPUARMState *env)
}
}
break;
+ case EXCP_SEMIHOST:
+ env->regs[0] = do_arm_semihosting(env);
+ break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 6695390075..9d75227e04 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -52,7 +52,7 @@
#define EXCP_SMC 13 /* Secure Monitor Call */
#define EXCP_VIRQ 14
#define EXCP_VFIQ 15
-#define EXCP_SEMIHOST 16 /* semihosting call (A64 only) */
+#define EXCP_SEMIHOST 16 /* semihosting call */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
diff --git a/target-arm/helper.c b/target-arm/helper.c
index cb83ee2008..25b15dc100 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -6573,12 +6573,19 @@ static inline bool check_for_semihosting(CPUState *cs)
/* Only intercept calls from privileged modes, to provide some
* semblance of security.
*/
- if (!semihosting_enabled() ||
- ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR)) {
+ if (cs->exception_index != EXCP_SEMIHOST &&
+ (!semihosting_enabled() ||
+ ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR))) {
return false;
}
switch (cs->exception_index) {
+ case EXCP_SEMIHOST:
+ /* This is always a semihosting call; the "is this usermode"
+ * and "is semihosting enabled" checks have been done at
+ * translate time.
+ */
+ break;
case EXCP_SWI:
/* Check for semihosting interrupt. */
if (env->thumb) {
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) {