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
This commit is contained in:
Peter Maydell 2014-10-24 12:19:13 +01:00
parent 3940433843
commit 37e6456ef5
3 changed files with 104 additions and 11 deletions

View File

@ -236,6 +236,16 @@ static inline uint32_t syn_aa32_svc(uint32_t imm16, bool is_thumb)
| (is_thumb ? 0 : ARM_EL_IL);
}
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_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);

View File

@ -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);

View File

@ -84,6 +84,8 @@ static inline int get_mem_index(DisasContext *s)
#define DISAS_EXC 6
/* WFE */
#define DISAS_WFE 7
#define DISAS_HVC 8
#define DISAS_SMC 9
#ifdef TARGET_AARCH64
void a64_translate_init(void);