diff mbox

[v4,08/33] target-arm: A32: Emulate the SMC instruction

Message ID 1404169773-20264-9-git-send-email-greg.bellows@linaro.org
State New
Headers show

Commit Message

Greg Bellows June 30, 2014, 11:09 p.m. UTC
From: Fabian Aggeler <aggelerf@ethz.ch>

Implements SMC instruction in Aarch32 using the A32 syndrome. When executing
SMC instruction from monitor CPU mode SCR.NS bit is reset.

Signed-off-by: Sergey Fedorov <s.fedorov@samsung.com>
Signed-off-by: Fabian Aggeler <aggelerf@ethz.ch>
Signed-off-by: Greg Bellows <greg.bellows@linaro.org>
---
 target-arm/helper.c    | 11 +++++++++++
 target-arm/internals.h |  5 +++++
 target-arm/translate.c | 35 +++++++++++++++++++++++++----------
 3 files changed, 41 insertions(+), 10 deletions(-)
diff mbox

Patch

diff --git a/target-arm/helper.c b/target-arm/helper.c
index ed1e3c7..2e285ab 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3603,6 +3603,12 @@  void arm_cpu_do_interrupt(CPUState *cs)
         mask = CPSR_A | CPSR_I | CPSR_F;
         offset = 4;
         break;
+    case EXCP_SMC:
+        new_mode = ARM_CPU_MODE_MON;
+        addr = 0x08;
+        mask = CPSR_A | CPSR_I | CPSR_F;
+        offset = 0;
+        break;
     default:
         cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
         return; /* Never happens.  Keep compiler happy.  */
@@ -3621,6 +3627,11 @@  void arm_cpu_do_interrupt(CPUState *cs)
          */
         addr += env->cp15.vbar_el[1];
     }
+
+    if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON) {
+        env->cp15.scr_el3 &= ~SCR_NS;
+    }
+
     switch_mode (env, new_mode);
     env->spsr = cpsr_read(env);
     /* Clear IT bits.  */
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 8815f7c..cda049a 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -224,6 +224,11 @@  static inline uint32_t syn_aa32_svc(uint16_t imm16, bool is_thumb)
         | (is_thumb ? 0 : ARM_EL_IL);
 }
 
+static inline uint32_t syn_aa32_smc(void)
+{
+    return (EC_AA32_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL;
+}
+
 static inline uint32_t syn_aa64_bkpt(uint16_t imm16)
 {
     return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | imm16;
diff --git a/target-arm/translate.c b/target-arm/translate.c
index bf17952..f657389 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -7840,15 +7840,25 @@  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) {
+            if (op1 == 1) {
+                /* bkpt */
+                ARCH(5);
+                gen_exception_insn(s, 4, EXCP_BKPT,
+                        syn_aa32_bkpt(imm16, false));
+            } else if (op1 == 3) {
+                /* smi/smc */
+                if (!arm_dc_feature(s, ARM_FEATURE_EL3) ||
+                        s->current_pl == 0) {
+                    goto illegal_op;
+                }
+                tmp = tcg_const_i32(syn_aa32_smc());
+                gen_set_pc_im(s, s->pc);
+                gen_helper_smc(cpu_env, tmp);
+                tcg_temp_free_i32(tmp);
+                break;
+            } else {
                 goto illegal_op;
             }
-            /* bkpt */
-            ARCH(5);
-            gen_exception_insn(s, 4, EXCP_BKPT, syn_aa32_bkpt(imm16, false));
             break;
         }
         case 0x8: /* signed multiply */
@@ -9679,9 +9689,14 @@  static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw
 
                 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 (!arm_dc_feature(s, ARM_FEATURE_EL3) ||
+                            s->current_pl == 0) {
+                        goto illegal_op;
+                    }
+                    tmp = tcg_const_i32(syn_aa32_smc());
+                    gen_set_pc_im(s, s->pc);
+                    gen_helper_smc(cpu_env, tmp);
+                    tcg_temp_free_i32(tmp);
                 } else {
                     op = (insn >> 20) & 7;
                     switch (op) {