Skip to content

Commit

Permalink
target-arm: Implement new HLT trap for semihosting
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pm215 committed Oct 24, 2016
1 parent caae803 commit 19a6e31
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 7 deletions.
3 changes: 3 additions & 0 deletions linux-user/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion target-arm/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions target-arm/helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
54 changes: 50 additions & 4 deletions target-arm/translate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 19a6e31

Please sign in to comment.