diff mbox

[RFC,20/21] target-arm: implement SMC instruction

Message ID 1386060535-15908-21-git-send-email-s.fedorov@samsung.com
State New
Headers show

Commit Message

Sergey Fedorov Dec. 3, 2013, 8:48 a.m. UTC
SMC instruction is implemented similar to SVC instruction. When
executing SMC instruction from monitor CPU mode SCR.NS bit is reset.

Signed-off-by: Sergey Fedorov <s.fedorov@samsung.com>
---
 target-arm/cpu.h       |    1 +
 target-arm/helper.c    |   11 +++++++++++
 target-arm/translate.c |   37 +++++++++++++++++++++++++++----------
 3 files changed, 39 insertions(+), 10 deletions(-)
diff mbox

Patch

diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 3e5b860..556d630 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -49,6 +49,7 @@ 
 #define EXCP_EXCEPTION_EXIT  8   /* Return from v7M exception.  */
 #define EXCP_KERNEL_TRAP     9   /* Jumped to kernel code page.  */
 #define EXCP_STREX          10
+#define EXCP_SMC            11   /* secure monitor call */
 
 #define ARMV7M_EXCP_RESET   1
 #define ARMV7M_EXCP_NMI     2
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 582de74..ba442c0 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2628,6 +2628,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(env, "Unhandled exception 0x%x\n", env->exception_index);
         return; /* Never happens.  Keep compiler happy.  */
@@ -2641,6 +2647,11 @@  void arm_cpu_do_interrupt(CPUState *cs)
     } else {
         addr += env->cp15.c12_vbar;
     }
+
+    if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_MON) {
+        env->cp15.c1_scr &= ~1 /* NS */;
+    }
+
     switch_mode (env, new_mode);
     env->spsr = cpsr_read(env);
     /* Clear IT bits.  */
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 345866c..b81f09a 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -66,6 +66,7 @@  static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
    conditional execution state has been updated.  */
 #define DISAS_WFI 4
 #define DISAS_SWI 5
+#define DISAS_SMC 6
 
 TCGv_ptr cpu_env;
 /* We reuse the same 64-bit temporaries for efficiency.  */
@@ -7105,15 +7106,21 @@  static void disas_arm_insn(CPUARMState * env, DisasContext *s)
             store_reg(s, rd, tmp);
             break;
         case 7:
-            /* 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);
+            } else if (op1 == 3) {
+                /* smi/smc */
+                if (!arm_feature(env, ARM_FEATURE_TRUSTZONE) || IS_USER(s)) {
+                    goto illegal_op;
+                }
+                gen_set_pc_im(s, s->pc);
+                s->is_jmp = DISAS_SMC;
+                break;
+            } else {
                 goto illegal_op;
             }
-            /* bkpt */
-            ARCH(5);
-            gen_exception_insn(s, 4, EXCP_BKPT);
             break;
         case 0x8: /* signed multiply */
         case 0xa:
@@ -8885,9 +8892,12 @@  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_feature(env, ARM_FEATURE_TRUSTZONE) ||
+                            IS_USER(s)) {
+                        goto illegal_op;
+                    }
+                    gen_set_pc_im(s, s->pc);
+                    s->is_jmp = DISAS_SMC;
                 } else {
                     op = (insn >> 20) & 7;
                     switch (op) {
@@ -10284,6 +10294,8 @@  static inline void gen_intermediate_code_internal(ARMCPU *cpu,
             gen_set_condexec(dc);
             if (dc->is_jmp == DISAS_SWI) {
                 gen_exception(EXCP_SWI);
+            } else if (dc->is_jmp == DISAS_SMC) {
+                gen_exception(EXCP_SMC);
             } else {
                 gen_exception(EXCP_DEBUG);
             }
@@ -10296,6 +10308,8 @@  static inline void gen_intermediate_code_internal(ARMCPU *cpu,
         gen_set_condexec(dc);
         if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
             gen_exception(EXCP_SWI);
+        } else if (dc->is_jmp == DISAS_SMC && !dc->condjmp) {
+            gen_exception(EXCP_SMC);
         } else {
             /* FIXME: Single stepping a WFI insn will not halt
                the CPU.  */
@@ -10330,6 +10344,9 @@  static inline void gen_intermediate_code_internal(ARMCPU *cpu,
         case DISAS_SWI:
             gen_exception(EXCP_SWI);
             break;
+        case DISAS_SMC:
+            gen_exception(EXCP_SMC);
+            break;
         }
         if (dc->condjmp) {
             gen_set_label(dc->condlabel);