aboutsummaryrefslogtreecommitdiff
path: root/target-arm/translate.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-10-24 12:19:13 +0100
committerPeter Maydell <peter.maydell@linaro.org>2014-10-24 12:19:13 +0100
commit37e6456ef539b2c4d1b9438f3df90eb032a9618f (patch)
treed7bc890b4fb05b19f12223ba63bad55005565ee6 /target-arm/translate.c
parent394043384337d3e84fe92ecc83bd90b0dcd661d5 (diff)
target-arm: Add support for A32 and T32 HVC and SMC insns
Add support for HVC and SMC instructions to the A32 and T32 decoder. Using these for real exceptions to EL2 or EL3 is currently not supported (the do_interrupt routine does not handle them) but we require the instruction support to implement PSCI. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1412865028-17725-6-git-send-email-peter.maydell@linaro.org
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r--target-arm/translate.c103
1 files changed, 92 insertions, 11 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 8a2994fcb4..4e764d340b 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -941,6 +941,39 @@ static inline void gen_set_pc_im(DisasContext *s, target_ulong val)
tcg_gen_movi_i32(cpu_R[15], val);
}
+static inline void gen_hvc(DisasContext *s, int imm16)
+{
+ /* The pre HVC helper handles cases when HVC gets trapped
+ * as an undefined insn by runtime configuration (ie before
+ * the insn really executes).
+ */
+ gen_set_pc_im(s, s->pc - 4);
+ gen_helper_pre_hvc(cpu_env);
+ /* Otherwise we will treat this as a real exception which
+ * happens after execution of the insn. (The distinction matters
+ * for the PC value reported to the exception handler and also
+ * for single stepping.)
+ */
+ s->svc_imm = imm16;
+ gen_set_pc_im(s, s->pc);
+ s->is_jmp = DISAS_HVC;
+}
+
+static inline void gen_smc(DisasContext *s)
+{
+ /* As with HVC, we may take an exception either before or after
+ * the insn executes.
+ */
+ TCGv_i32 tmp;
+
+ gen_set_pc_im(s, s->pc - 4);
+ tmp = tcg_const_i32(syn_aa32_smc());
+ gen_helper_pre_smc(cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ gen_set_pc_im(s, s->pc);
+ s->is_jmp = DISAS_SMC;
+}
+
static inline void
gen_set_condexec (DisasContext *s)
{
@@ -7872,15 +7905,32 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s)
case 7:
{
int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4);
- /* SMC instruction (op1 == 3)
- and undefined instructions (op1 == 0 || op1 == 2)
- will trap */
- if (op1 != 1) {
+ switch (op1) {
+ case 1:
+ /* bkpt */
+ ARCH(5);
+ gen_exception_insn(s, 4, EXCP_BKPT,
+ syn_aa32_bkpt(imm16, false));
+ break;
+ case 2:
+ /* Hypervisor call (v7) */
+ ARCH(7);
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_hvc(s, imm16);
+ break;
+ case 3:
+ /* Secure monitor call (v6+) */
+ ARCH(6K);
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_smc(s);
+ break;
+ default:
goto illegal_op;
}
- /* bkpt */
- ARCH(5);
- gen_exception_insn(s, 4, EXCP_BKPT, syn_aa32_bkpt(imm16, false));
break;
}
case 0x8: /* signed multiply */
@@ -9710,10 +9760,23 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
goto illegal_op;
if (insn & (1 << 26)) {
- /* Secure monitor call (v6Z) */
- qemu_log_mask(LOG_UNIMP,
- "arm: unimplemented secure monitor call\n");
- goto illegal_op; /* not implemented. */
+ if (!(insn & (1 << 20))) {
+ /* Hypervisor call (v7) */
+ int imm16 = extract32(insn, 16, 4) << 12
+ | extract32(insn, 0, 12);
+ ARCH(7);
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_hvc(s, imm16);
+ } else {
+ /* Secure monitor call (v6+) */
+ ARCH(6K);
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_smc(s);
+ }
} else {
op = (insn >> 20) & 7;
switch (op) {
@@ -11148,6 +11211,12 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
if (dc->is_jmp == DISAS_SWI) {
gen_ss_advance(dc);
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
+ } else if (dc->is_jmp == DISAS_HVC) {
+ gen_ss_advance(dc);
+ gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm));
+ } else if (dc->is_jmp == DISAS_SMC) {
+ gen_ss_advance(dc);
+ gen_exception(EXCP_SMC, syn_aa32_smc());
} else if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
@@ -11163,6 +11232,12 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
gen_ss_advance(dc);
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
+ } else if (dc->is_jmp == DISAS_HVC && !dc->condjmp) {
+ gen_ss_advance(dc);
+ gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm));
+ } else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
+ gen_ss_advance(dc);
+ gen_exception(EXCP_SMC, syn_aa32_smc());
} else if (dc->ss_active) {
gen_step_complete_exception(dc);
} else {
@@ -11202,6 +11277,12 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu,
case DISAS_SWI:
gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb));
break;
+ case DISAS_HVC:
+ gen_exception(EXCP_HVC, syn_aa32_hvc(dc->svc_imm));
+ break;
+ case DISAS_SMC:
+ gen_exception(EXCP_SMC, syn_aa32_smc());
+ break;
}
if (dc->condjmp) {
gen_set_label(dc->condlabel);