diff mbox

[v2,3/5] target-arm: add hvc and smc exception emulation handling infrastructure

Message ID 1409572529-14897-4-git-send-email-ard.biesheuvel@linaro.org
State New
Headers show

Commit Message

Ard Biesheuvel Sept. 1, 2014, 11:55 a.m. UTC
From: Rob Herring <rob.herring@linaro.org>

Add the infrastructure to handle and emulate hvc and smc exceptions.
This will enable emulation of things such as PSCI calls. This commit
does not change the behavior and will exit with unknown exception.

Signed-off-by: Rob Herring <rob.herring@linaro.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 target-arm/cpu-qom.h       |  3 +++
 target-arm/cpu.h           |  2 ++
 target-arm/helper-a64.c    | 16 ++++++++++++++++
 target-arm/helper.c        | 32 ++++++++++++++++++++++++++++++++
 target-arm/internals.h     | 20 ++++++++++++++++++++
 target-arm/translate-a64.c | 26 +++++++++++++++++---------
 target-arm/translate.c     | 24 +++++++++++++++++-------
 7 files changed, 107 insertions(+), 16 deletions(-)

Comments

Ard Biesheuvel Sept. 1, 2014, 6:01 p.m. UTC | #1
On 1 September 2014 13:55, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> From: Rob Herring <rob.herring@linaro.org>
>
> Add the infrastructure to handle and emulate hvc and smc exceptions.
> This will enable emulation of things such as PSCI calls. This commit
> does not change the behavior and will exit with unknown exception.
>
> Signed-off-by: Rob Herring <rob.herring@linaro.org>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  target-arm/cpu-qom.h       |  3 +++
>  target-arm/cpu.h           |  2 ++
>  target-arm/helper-a64.c    | 16 ++++++++++++++++
>  target-arm/helper.c        | 32 ++++++++++++++++++++++++++++++++
>  target-arm/internals.h     | 20 ++++++++++++++++++++
>  target-arm/translate-a64.c | 26 +++++++++++++++++---------
>  target-arm/translate.c     | 24 +++++++++++++++++-------
>  7 files changed, 107 insertions(+), 16 deletions(-)
>
> diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
> index eae0a7b9c908..104cc67e82d2 100644
> --- a/target-arm/cpu-qom.h
> +++ b/target-arm/cpu-qom.h
> @@ -192,6 +192,9 @@ extern const struct VMStateDescription vmstate_arm_cpu;
>  void register_cp_regs_for_features(ARMCPU *cpu);
>  void init_cpreg_list(ARMCPU *cpu);
>
> +bool arm_cpu_do_hvc(CPUState *cs);
> +bool arm_cpu_do_smc(CPUState *cs);
> +
>  void arm_cpu_do_interrupt(CPUState *cpu);
>  void arm_v7m_cpu_do_interrupt(CPUState *cpu);
>
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 51bedc826299..d235929f4c12 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -51,6 +51,8 @@
>  #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_HVC            11
> +#define EXCP_SMC            12
>
>  #define ARMV7M_EXCP_RESET   1
>  #define ARMV7M_EXCP_NMI     2
> diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
> index 89b913ee9396..1f8072ab141b 100644
> --- a/target-arm/helper-a64.c
> +++ b/target-arm/helper-a64.c
> @@ -485,6 +485,22 @@ void aarch64_cpu_do_interrupt(CPUState *cs)
>      case EXCP_FIQ:
>          addr += 0x100;
>          break;
> +    case EXCP_HVC:
> +        if (arm_cpu_do_hvc(cs)) {
> +            return;
> +        }
> +        /* Treat as unallocated encoding */
> +        qemu_log_mask(LOG_GUEST_ERROR, "HVC not implemented on this CPU\n");
> +        env->exception.syndrome = syn_uncategorized();
> +        break;
> +    case EXCP_SMC:
> +        if (arm_cpu_do_smc(cs)) {
> +            return;
> +        }
> +        /* Treat as unallocated encoding */
> +        qemu_log_mask(LOG_GUEST_ERROR, "SMC not implemented on this CPU\n");
> +        env->exception.syndrome = syn_uncategorized();
> +        break;
>      default:
>          cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
>      }
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index 2b95f33872cb..51a01a815b7b 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -3497,6 +3497,16 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
>      env->thumb = addr & 1;
>  }
>
> +bool arm_cpu_do_hvc(CPUState *cs)
> +{
> +    return false;
> +}
> +
> +bool arm_cpu_do_smc(CPUState *cs)
> +{
> +    return false;
> +}
> +
>  /* Handle a CPU exception.  */
>  void arm_cpu_do_interrupt(CPUState *cs)
>  {
> @@ -3599,6 +3609,28 @@ void arm_cpu_do_interrupt(CPUState *cs)
>          mask = CPSR_A | CPSR_I | CPSR_F;
>          offset = 4;
>          break;
> +    case EXCP_HVC:
> +        if (arm_cpu_do_hvc(cs)) {
> +            return;
> +        }
> +        qemu_log_mask(LOG_GUEST_ERROR, "HVC not implemented on this CPU\n");
> +        goto hvc_unallocated;
> +    case EXCP_SMC:
> +        if (arm_cpu_do_smc(cs)) {
> +            return;
> +        }
> +        qemu_log_mask(LOG_GUEST_ERROR, "SMC not implemented on this CPU\n");
> +    hvc_unallocated:
> +        /* Treat as unallocated encoding */
> +        new_mode = ARM_CPU_MODE_UND;
> +        addr = 0x04;
> +        mask = CPSR_I;
> +        if (env->thumb) {
> +            offset = 2;
> +        } else {
> +            offset = 4;
> +        }
> +        break;

Replying to self: I guess I forgot to set the correct ESR value here, would this

env->cp15.esr_el[1] = syn_uncategorized();

be sufficient?


>      default:
>          cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
>          return; /* Never happens.  Keep compiler happy.  */
> diff --git a/target-arm/internals.h b/target-arm/internals.h
> index 53c2e3cf3e7e..caab98e6b508 100644
> --- a/target-arm/internals.h
> +++ b/target-arm/internals.h
> @@ -210,6 +210,26 @@ static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_thumb)
>          | (is_thumb ? 0 : ARM_EL_IL);
>  }
>
> +static inline uint32_t syn_aa64_hvc(uint32_t imm16)
> +{
> +    return (EC_AA64_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
> +}
> +
> +static inline uint32_t syn_aa32_hvc(uint32_t imm16)
> +{
> +    return (EC_AA32_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
> +}
> +
> +static inline uint32_t syn_aa64_smc(uint32_t imm16)
> +{
> +    return (EC_AA64_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
> +}
> +
> +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(uint32_t imm16)
>  {
>      return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
> diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
> index 8e66b6c97282..65e35e3aaec0 100644
> --- a/target-arm/translate-a64.c
> +++ b/target-arm/translate-a64.c
> @@ -1473,20 +1473,28 @@ static void disas_exc(DisasContext *s, uint32_t insn)
>
>      switch (opc) {
>      case 0:
> -        /* SVC, HVC, SMC; since we don't support the Virtualization
> -         * or TrustZone extensions these all UNDEF except SVC.
> -         */
> -        if (op2_ll != 1) {
> -            unallocated_encoding(s);
> -            break;
> -        }
>          /* For SVC, HVC and SMC we advance the single-step state
>           * machine before taking the exception. This is architecturally
>           * mandated, to ensure that single-stepping a system call
>           * instruction works properly.
>           */
> -        gen_ss_advance(s);
> -        gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16));
> +        switch (op2_ll) {
> +        case 1:
> +            gen_ss_advance(s);
> +            gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16));
> +            break;
> +        case 2:
> +            gen_ss_advance(s);
> +            gen_exception_insn(s, 0, EXCP_HVC, syn_aa64_hvc(imm16));
> +            break;
> +        case 3:
> +            gen_ss_advance(s);
> +            gen_exception_insn(s, 0, EXCP_SMC, syn_aa64_smc(imm16));
> +            break;
> +        default:
> +            unallocated_encoding(s);
> +            break;
> +        }
>          break;
>      case 1:
>          if (op2_ll != 0) {
> diff --git a/target-arm/translate.c b/target-arm/translate.c
> index 2c0b1deaea81..a4545ed2bc40 100644
> --- a/target-arm/translate.c
> +++ b/target-arm/translate.c
> @@ -7871,9 +7871,14 @@ 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 */
> +            /* HVC and SMC instructions */
> +            if (op1 == 2) {
> +                gen_exception_insn(s, 0, EXCP_HVC, syn_aa32_hvc(imm16));
> +                break;
> +            } else if (op1 == 3) {
> +                gen_exception_insn(s, 0, EXCP_SMC, syn_aa32_smc());
> +                break;
> +            }
>              if (op1 != 1) {
>                  goto illegal_op;
>              }
> @@ -9709,10 +9714,15 @@ 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) */
> +                        uint32_t imm16 = extract32(insn, 16, 4) << 12;
> +                        imm16 |= extract32(insn, 0, 12);
> +                        gen_exception_insn(s, 0, EXCP_HVC, syn_aa32_hvc(imm16));
> +                    } else {
> +                        /* Secure monitor call (v6+) */
> +                        gen_exception_insn(s, 0, EXCP_SMC, syn_aa32_smc());
> +                    }
>                  } else {
>                      op = (insn >> 20) & 7;
>                      switch (op) {
> --
> 1.8.3.2
>
Peter Maydell Sept. 1, 2014, 6:33 p.m. UTC | #2
On 1 September 2014 19:01, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 1 September 2014 13:55, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> @@ -3599,6 +3609,28 @@ void arm_cpu_do_interrupt(CPUState *cs)
>>          mask = CPSR_A | CPSR_I | CPSR_F;
>>          offset = 4;
>>          break;
>> +    case EXCP_HVC:
>> +        if (arm_cpu_do_hvc(cs)) {
>> +            return;
>> +        }
>> +        qemu_log_mask(LOG_GUEST_ERROR, "HVC not implemented on this CPU\n");
>> +        goto hvc_unallocated;
>> +    case EXCP_SMC:
>> +        if (arm_cpu_do_smc(cs)) {
>> +            return;
>> +        }
>> +        qemu_log_mask(LOG_GUEST_ERROR, "SMC not implemented on this CPU\n");
>> +    hvc_unallocated:
>> +        /* Treat as unallocated encoding */
>> +        new_mode = ARM_CPU_MODE_UND;
>> +        addr = 0x04;
>> +        mask = CPSR_I;
>> +        if (env->thumb) {
>> +            offset = 2;
>> +        } else {
>> +            offset = 4;
>> +        }
>> +        break;
>
> Replying to self: I guess I forgot to set the correct ESR value here, would this
>
> env->cp15.esr_el[1] = syn_uncategorized();
>
> be sufficient?

That's not necessary in the AArch32 do_interrupt code:
AArch32 doesn't have a syndrome register, and the
esr_el[1] holds the AArch32 DFSR value. DFSR isn't
set on UNDEF exceptions, so we should leave esr_el[1]
unchanged.

thanks
-- PMM
diff mbox

Patch

diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index eae0a7b9c908..104cc67e82d2 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -192,6 +192,9 @@  extern const struct VMStateDescription vmstate_arm_cpu;
 void register_cp_regs_for_features(ARMCPU *cpu);
 void init_cpreg_list(ARMCPU *cpu);
 
+bool arm_cpu_do_hvc(CPUState *cs);
+bool arm_cpu_do_smc(CPUState *cs);
+
 void arm_cpu_do_interrupt(CPUState *cpu);
 void arm_v7m_cpu_do_interrupt(CPUState *cpu);
 
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 51bedc826299..d235929f4c12 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -51,6 +51,8 @@ 
 #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_HVC            11
+#define EXCP_SMC            12
 
 #define ARMV7M_EXCP_RESET   1
 #define ARMV7M_EXCP_NMI     2
diff --git a/target-arm/helper-a64.c b/target-arm/helper-a64.c
index 89b913ee9396..1f8072ab141b 100644
--- a/target-arm/helper-a64.c
+++ b/target-arm/helper-a64.c
@@ -485,6 +485,22 @@  void aarch64_cpu_do_interrupt(CPUState *cs)
     case EXCP_FIQ:
         addr += 0x100;
         break;
+    case EXCP_HVC:
+        if (arm_cpu_do_hvc(cs)) {
+            return;
+        }
+        /* Treat as unallocated encoding */
+        qemu_log_mask(LOG_GUEST_ERROR, "HVC not implemented on this CPU\n");
+        env->exception.syndrome = syn_uncategorized();
+        break;
+    case EXCP_SMC:
+        if (arm_cpu_do_smc(cs)) {
+            return;
+        }
+        /* Treat as unallocated encoding */
+        qemu_log_mask(LOG_GUEST_ERROR, "SMC not implemented on this CPU\n");
+        env->exception.syndrome = syn_uncategorized();
+        break;
     default:
         cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
     }
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 2b95f33872cb..51a01a815b7b 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3497,6 +3497,16 @@  void arm_v7m_cpu_do_interrupt(CPUState *cs)
     env->thumb = addr & 1;
 }
 
+bool arm_cpu_do_hvc(CPUState *cs)
+{
+    return false;
+}
+
+bool arm_cpu_do_smc(CPUState *cs)
+{
+    return false;
+}
+
 /* Handle a CPU exception.  */
 void arm_cpu_do_interrupt(CPUState *cs)
 {
@@ -3599,6 +3609,28 @@  void arm_cpu_do_interrupt(CPUState *cs)
         mask = CPSR_A | CPSR_I | CPSR_F;
         offset = 4;
         break;
+    case EXCP_HVC:
+        if (arm_cpu_do_hvc(cs)) {
+            return;
+        }
+        qemu_log_mask(LOG_GUEST_ERROR, "HVC not implemented on this CPU\n");
+        goto hvc_unallocated;
+    case EXCP_SMC:
+        if (arm_cpu_do_smc(cs)) {
+            return;
+        }
+        qemu_log_mask(LOG_GUEST_ERROR, "SMC not implemented on this CPU\n");
+    hvc_unallocated:
+        /* Treat as unallocated encoding */
+        new_mode = ARM_CPU_MODE_UND;
+        addr = 0x04;
+        mask = CPSR_I;
+        if (env->thumb) {
+            offset = 2;
+        } else {
+            offset = 4;
+        }
+        break;
     default:
         cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
         return; /* Never happens.  Keep compiler happy.  */
diff --git a/target-arm/internals.h b/target-arm/internals.h
index 53c2e3cf3e7e..caab98e6b508 100644
--- a/target-arm/internals.h
+++ b/target-arm/internals.h
@@ -210,6 +210,26 @@  static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_thumb)
         | (is_thumb ? 0 : ARM_EL_IL);
 }
 
+static inline uint32_t syn_aa64_hvc(uint32_t imm16)
+{
+    return (EC_AA64_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
+}
+
+static inline uint32_t syn_aa32_hvc(uint32_t imm16)
+{
+    return (EC_AA32_HVC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
+}
+
+static inline uint32_t syn_aa64_smc(uint32_t imm16)
+{
+    return (EC_AA64_SMC << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
+}
+
+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(uint32_t imm16)
 {
     return (EC_AA64_BKPT << ARM_EL_EC_SHIFT) | ARM_EL_IL | (imm16 & 0xffff);
diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c
index 8e66b6c97282..65e35e3aaec0 100644
--- a/target-arm/translate-a64.c
+++ b/target-arm/translate-a64.c
@@ -1473,20 +1473,28 @@  static void disas_exc(DisasContext *s, uint32_t insn)
 
     switch (opc) {
     case 0:
-        /* SVC, HVC, SMC; since we don't support the Virtualization
-         * or TrustZone extensions these all UNDEF except SVC.
-         */
-        if (op2_ll != 1) {
-            unallocated_encoding(s);
-            break;
-        }
         /* For SVC, HVC and SMC we advance the single-step state
          * machine before taking the exception. This is architecturally
          * mandated, to ensure that single-stepping a system call
          * instruction works properly.
          */
-        gen_ss_advance(s);
-        gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16));
+        switch (op2_ll) {
+        case 1:
+            gen_ss_advance(s);
+            gen_exception_insn(s, 0, EXCP_SWI, syn_aa64_svc(imm16));
+            break;
+        case 2:
+            gen_ss_advance(s);
+            gen_exception_insn(s, 0, EXCP_HVC, syn_aa64_hvc(imm16));
+            break;
+        case 3:
+            gen_ss_advance(s);
+            gen_exception_insn(s, 0, EXCP_SMC, syn_aa64_smc(imm16));
+            break;
+        default:
+            unallocated_encoding(s);
+            break;
+        }
         break;
     case 1:
         if (op2_ll != 0) {
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 2c0b1deaea81..a4545ed2bc40 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -7871,9 +7871,14 @@  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 */
+            /* HVC and SMC instructions */
+            if (op1 == 2) {
+                gen_exception_insn(s, 0, EXCP_HVC, syn_aa32_hvc(imm16));
+                break;
+            } else if (op1 == 3) {
+                gen_exception_insn(s, 0, EXCP_SMC, syn_aa32_smc());
+                break;
+            }
             if (op1 != 1) {
                 goto illegal_op;
             }
@@ -9709,10 +9714,15 @@  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) */
+                        uint32_t imm16 = extract32(insn, 16, 4) << 12;
+                        imm16 |= extract32(insn, 0, 12);
+                        gen_exception_insn(s, 0, EXCP_HVC, syn_aa32_hvc(imm16));
+                    } else {
+                        /* Secure monitor call (v6+) */
+                        gen_exception_insn(s, 0, EXCP_SMC, syn_aa32_smc());
+                    }
                 } else {
                     op = (insn >> 20) & 7;
                     switch (op) {