Message ID | 1409572529-14897-4-git-send-email-ard.biesheuvel@linaro.org |
---|---|
State | New |
Headers | show |
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 >
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 --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) {