From 52c16b458ab2f8766867bc5fc099b0a604a36f77 Mon Sep 17 00:00:00 2001 From: Nathan Rossi <nathan@nathanrossi.com> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 01/24] arm: xlnx-zynqmp: Fix up GIC region size The GIC in ZynqMP cover a 64K address space, however the actual registers are decoded within a 4K address space and mirrored at the 4K boundaries. This change fixes the defined size for these regions as it was set to 0x4000/16K incorrectly. Signed-off-by: Nathan Rossi <nathan@nathanrossi.com> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1441719672-25296-1-git-send-email-nathan@nathanrossi.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- include/hw/arm/xlnx-zynqmp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 97622ecf8f..4005a9960b 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -46,7 +46,7 @@ * number of memory region aliases. */ -#define XLNX_ZYNQMP_GIC_REGION_SIZE 0x4000 +#define XLNX_ZYNQMP_GIC_REGION_SIZE 0x1000 #define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE - 1) typedef struct XlnxZynqMPState { From 24cfc8dc583db57303137fd41f9f42806ea315a0 Mon Sep 17 00:00:00 2001 From: Alistair Francis <alistair.francis@xilinx.com> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 02/24] xlnx-zynqmp: Remove unnecessary brackets around error messages The errp and err variable have unnecessary brackets around them, so remove the brackets. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com> Message-id: 9900393572b63f2ec3d68785ca98193d81e0ac71.1441758563.git.alistair.francis@xilinx.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/xlnx-zynqmp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 2955f3b866..21855420dd 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -128,7 +128,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", XLNX_ZYNQMP_NUM_APU_CPUS); object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } assert(ARRAY_SIZE(xlnx_zynqmp_gic_regions) == XLNX_ZYNQMP_GIC_REGIONS); @@ -173,7 +173,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->apu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } @@ -206,7 +206,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->rpu_cpu[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } } @@ -229,7 +229,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } object_property_set_bool(OBJECT(&s->gem[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]); @@ -240,7 +240,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { - error_propagate((errp), (err)); + error_propagate(errp, err); return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, uart_addr[i]); From 78bcaa3e37afbd0c5316634f917c13487384b6ca Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 03/24] target-arm: Share all common TCG temporaries This is a bug fix for aarch64. At present, we have branches using the 32-bit (translate.c) versions of cpu_[NZCV]F, but we set the flags using the 64-bit (translate-a64.c) versions of cpu_[NZCV]F. From the view of the TCG code generator, these are unrelated variables. The bug is hard to see because we currently only read these variables from branches, and upon reaching a branch TCG will first spill live variables and then reload the arguments of the branch. Since the 32-bit versions were never live until reaching the branch, we'd re-read the data that had just been spilled from the 64-bit versions. There is currently no such problem with the cpu_exclusive_* variables, but there's no point in tempting fate. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-2-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 22 ---------------------- target-arm/translate.c | 10 +++++----- target-arm/translate.h | 8 ++++++++ 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index faece2cd43..bb70185eae 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -40,16 +40,9 @@ static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; -static TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; /* Load/store exclusive handling */ -static TCGv_i64 cpu_exclusive_addr; -static TCGv_i64 cpu_exclusive_val; static TCGv_i64 cpu_exclusive_high; -#ifdef CONFIG_USER_ONLY -static TCGv_i64 cpu_exclusive_test; -static TCGv_i32 cpu_exclusive_info; -#endif static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", @@ -105,23 +98,8 @@ void a64_translate_init(void) regnames[i]); } - cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF"); - cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF"); - cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF"); - cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF"); - - cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_val), "exclusive_val"); cpu_exclusive_high = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUARMState, exclusive_high), "exclusive_high"); -#ifdef CONFIG_USER_ONLY - cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUARMState, exclusive_test), "exclusive_test"); - cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0, - offsetof(CPUARMState, exclusive_info), "exclusive_info"); -#endif } static inline ARMMMUIdx get_a64_user_mem_index(DisasContext *s) diff --git a/target-arm/translate.c b/target-arm/translate.c index ae705775d1..f1b7c16302 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -64,12 +64,12 @@ TCGv_ptr cpu_env; /* We reuse the same 64-bit temporaries for efficiency. */ static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; static TCGv_i32 cpu_R[16]; -static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; -static TCGv_i64 cpu_exclusive_addr; -static TCGv_i64 cpu_exclusive_val; +TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; +TCGv_i64 cpu_exclusive_addr; +TCGv_i64 cpu_exclusive_val; #ifdef CONFIG_USER_ONLY -static TCGv_i64 cpu_exclusive_test; -static TCGv_i32 cpu_exclusive_info; +TCGv_i64 cpu_exclusive_test; +TCGv_i32 cpu_exclusive_info; #endif /* FIXME: These should be removed. */ diff --git a/target-arm/translate.h b/target-arm/translate.h index 4b618a4c85..a30a1db361 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -63,7 +63,15 @@ typedef struct DisasContext { TCGv_i64 tmp_a64[TMP_A64_MAX]; } DisasContext; +/* Share the TCG temporaries common between 32 and 64 bit modes. */ extern TCGv_ptr cpu_env; +extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; +extern TCGv_i64 cpu_exclusive_addr; +extern TCGv_i64 cpu_exclusive_val; +#ifdef CONFIG_USER_ONLY +extern TCGv_i64 cpu_exclusive_test; +extern TCGv_i32 cpu_exclusive_info; +#endif static inline int arm_dc_feature(DisasContext *dc, int feature) { From 6c2c63d3a02c79e9035ca0370cc549d0f938a4dd Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 04/24] target-arm: Introduce DisasCompare Split arm_gen_test_cc into 3 functions, so that it can be reused for non-branch TCG comparisons. Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-3-git-send-email-rth@twiddle.net Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate.c | 115 ++++++++++++++++++++++++----------------- target-arm/translate.h | 9 ++++ 2 files changed, 78 insertions(+), 46 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index f1b7c16302..7d2e9848c4 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -738,81 +738,104 @@ static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) #undef PAS_OP /* - * generate a conditional branch based on ARM condition code cc. + * Generate a conditional based on ARM condition code cc. * This is common between ARM and Aarch64 targets. */ -void arm_gen_test_cc(int cc, TCGLabel *label) +void arm_test_cc(DisasCompare *cmp, int cc) { - TCGv_i32 tmp; - TCGLabel *inv; + TCGv_i32 value; + TCGCond cond; + bool global = true; switch (cc) { case 0: /* eq: Z */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); - break; case 1: /* ne: !Z */ - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); + cond = TCG_COND_EQ; + value = cpu_ZF; break; + case 2: /* cs: C */ - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_CF, 0, label); - break; case 3: /* cc: !C */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); + cond = TCG_COND_NE; + value = cpu_CF; break; + case 4: /* mi: N */ - tcg_gen_brcondi_i32(TCG_COND_LT, cpu_NF, 0, label); - break; case 5: /* pl: !N */ - tcg_gen_brcondi_i32(TCG_COND_GE, cpu_NF, 0, label); + cond = TCG_COND_LT; + value = cpu_NF; break; + case 6: /* vs: V */ - tcg_gen_brcondi_i32(TCG_COND_LT, cpu_VF, 0, label); - break; case 7: /* vc: !V */ - tcg_gen_brcondi_i32(TCG_COND_GE, cpu_VF, 0, label); + cond = TCG_COND_LT; + value = cpu_VF; break; + case 8: /* hi: C && !Z */ - inv = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, inv); - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); - gen_set_label(inv); - break; - case 9: /* ls: !C || Z */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); + case 9: /* ls: !C || Z -> !(C && !Z) */ + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + global = false; + /* CF is 1 for C, so -CF is an all-bits-set mask for C; + ZF is non-zero for !Z; so AND the two subexpressions. */ + tcg_gen_neg_i32(value, cpu_CF); + tcg_gen_and_i32(value, value, cpu_ZF); break; + case 10: /* ge: N == V -> N ^ V == 0 */ - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); - tcg_temp_free_i32(tmp); - break; case 11: /* lt: N != V -> N ^ V != 0 */ - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); - tcg_temp_free_i32(tmp); + /* Since we're only interested in the sign bit, == 0 is >= 0. */ + cond = TCG_COND_GE; + value = tcg_temp_new_i32(); + global = false; + tcg_gen_xor_i32(value, cpu_VF, cpu_NF); break; + case 12: /* gt: !Z && N == V */ - inv = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, inv); - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); - tcg_temp_free_i32(tmp); - gen_set_label(inv); - break; case 13: /* le: Z || N != V */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); - tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); - tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); - tcg_temp_free_i32(tmp); + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + global = false; + /* (N == V) is equal to the sign bit of ~(NF ^ VF). Propagate + * the sign bit then AND with ZF to yield the result. */ + tcg_gen_xor_i32(value, cpu_VF, cpu_NF); + tcg_gen_sari_i32(value, value, 31); + tcg_gen_andc_i32(value, cpu_ZF, value); break; + default: fprintf(stderr, "Bad condition code 0x%x\n", cc); abort(); } + + if (cc & 1) { + cond = tcg_invert_cond(cond); + } + + cmp->cond = cond; + cmp->value = value; + cmp->value_global = global; +} + +void arm_free_cc(DisasCompare *cmp) +{ + if (!cmp->value_global) { + tcg_temp_free_i32(cmp->value); + } +} + +void arm_jump_cc(DisasCompare *cmp, TCGLabel *label) +{ + tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label); +} + +void arm_gen_test_cc(int cc, TCGLabel *label) +{ + DisasCompare cmp; + arm_test_cc(&cmp, cc); + arm_jump_cc(&cmp, label); + arm_free_cc(&cmp); } static const uint8_t table_logic_cc[16] = { diff --git a/target-arm/translate.h b/target-arm/translate.h index a30a1db361..b8fe37a0a7 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -63,6 +63,12 @@ typedef struct DisasContext { TCGv_i64 tmp_a64[TMP_A64_MAX]; } DisasContext; +typedef struct DisasCompare { + TCGCond cond; + TCGv_i32 value; + bool value_global; +} DisasCompare; + /* Share the TCG temporaries common between 32 and 64 bit modes. */ extern TCGv_ptr cpu_env; extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; @@ -144,6 +150,9 @@ static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, } #endif +void arm_test_cc(DisasCompare *cmp, int cc); +void arm_free_cc(DisasCompare *cmp); +void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); #endif /* TARGET_ARM_TRANSLATE_H */ From 9305eac09e61d857c9cc11e20db754dfc25a82db Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 05/24] target-arm: Handle always condition codes within arm_test_cc Handling this with TCG_COND_ALWAYS will allow these unlikely cases to be handled without special cases in the rest of the translator. The TCG optimizer ought to be able to reduce these ALWAYS conditions completely. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-4-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target-arm/translate.c b/target-arm/translate.c index 7d2e9848c4..84a21ace54 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -804,6 +804,14 @@ void arm_test_cc(DisasCompare *cmp, int cc) tcg_gen_andc_i32(value, cpu_ZF, value); break; + case 14: /* always */ + case 15: /* always */ + /* Use the ALWAYS condition, which will fold early. + * It doesn't matter what we use for the value. */ + cond = TCG_COND_ALWAYS; + value = cpu_ZF; + goto no_invert; + default: fprintf(stderr, "Bad condition code 0x%x\n", cc); abort(); @@ -813,6 +821,7 @@ void arm_test_cc(DisasCompare *cmp, int cc) cond = tcg_invert_cond(cond); } + no_invert: cmp->cond = cond; cmp->value = value; cmp->value_global = global; From 259cb68491ab36427e7e5d820fe543d53b006ec6 Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:47 +0100 Subject: [PATCH 06/24] target-arm: Use setcond and movcond for csel Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-5-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 85 ++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index bb70185eae..1ab2c8d9aa 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -167,6 +167,31 @@ void gen_a64_set_pc_im(uint64_t val) tcg_gen_movi_i64(cpu_pc, val); } +typedef struct DisasCompare64 { + TCGCond cond; + TCGv_i64 value; +} DisasCompare64; + +static void a64_test_cc(DisasCompare64 *c64, int cc) +{ + DisasCompare c32; + + arm_test_cc(&c32, cc); + + /* Sign-extend the 32-bit value so that the GE/LT comparisons work + * properly. The NE/EQ comparisons are also fine with this choice. */ + c64->cond = c32.cond; + c64->value = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(c64->value, c32.value); + + arm_free_cc(&c32); +} + +static void a64_free_cc(DisasCompare64 *c64) +{ + tcg_temp_free_i64(c64->value); +} + static void gen_exception_internal(int excp) { TCGv_i32 tcg_excp = tcg_const_i32(excp); @@ -3607,7 +3632,8 @@ static void disas_cc(DisasContext *s, uint32_t insn) static void disas_cond_select(DisasContext *s, uint32_t insn) { unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; - TCGv_i64 tcg_rd, tcg_src; + TCGv_i64 tcg_rd, zero; + DisasCompare64 c; if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { /* S == 1 or op2<1> == 1 */ @@ -3622,48 +3648,35 @@ static void disas_cond_select(DisasContext *s, uint32_t insn) rn = extract32(insn, 5, 5); rd = extract32(insn, 0, 5); - if (rd == 31) { - /* silly no-op write; until we use movcond we must special-case - * this to avoid a dead temporary across basic blocks. - */ - return; - } - tcg_rd = cpu_reg(s, rd); - if (cond >= 0x0e) { /* condition "always" */ - tcg_src = read_cpu_reg(s, rn, sf); - tcg_gen_mov_i64(tcg_rd, tcg_src); + a64_test_cc(&c, cond); + zero = tcg_const_i64(0); + + if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { + /* CSET & CSETM. */ + tcg_gen_setcond_i64(tcg_invert_cond(c.cond), tcg_rd, c.value, zero); + if (else_inv) { + tcg_gen_neg_i64(tcg_rd, tcg_rd); + } } else { - /* OPTME: we could use movcond here, at the cost of duplicating - * a lot of the arm_gen_test_cc() logic. - */ - TCGLabel *label_match = gen_new_label(); - TCGLabel *label_continue = gen_new_label(); - - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - tcg_src = cpu_reg(s, rm); - + TCGv_i64 t_true = cpu_reg(s, rn); + TCGv_i64 t_false = read_cpu_reg(s, rm, 1); if (else_inv && else_inc) { - tcg_gen_neg_i64(tcg_rd, tcg_src); + tcg_gen_neg_i64(t_false, t_false); } else if (else_inv) { - tcg_gen_not_i64(tcg_rd, tcg_src); + tcg_gen_not_i64(t_false, t_false); } else if (else_inc) { - tcg_gen_addi_i64(tcg_rd, tcg_src, 1); - } else { - tcg_gen_mov_i64(tcg_rd, tcg_src); + tcg_gen_addi_i64(t_false, t_false, 1); } - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - tcg_gen_br(label_continue); - /* match: */ - gen_set_label(label_match); - tcg_src = read_cpu_reg(s, rn, sf); - tcg_gen_mov_i64(tcg_rd, tcg_src); - /* continue: */ - gen_set_label(label_continue); + tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); + } + + tcg_temp_free_i64(zero); + a64_free_cc(&c); + + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } } From 7dd03d773e0dafae9271318fc8d6b2b14de74403 Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:48 +0100 Subject: [PATCH 07/24] target-arm: Implement ccmp branchless This can allow much of a ccmp to be elided when particular flags are subsequently dead. Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-6-git-send-email-rth@twiddle.net Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 74 +++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 1ab2c8d9aa..b48cd97314 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -3570,8 +3570,9 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) static void disas_cc(DisasContext *s, uint32_t insn) { unsigned int sf, op, y, cond, rn, nzcv, is_imm; - TCGLabel *label_continue = NULL; + TCGv_i32 tcg_t0, tcg_t1, tcg_t2; TCGv_i64 tcg_tmp, tcg_y, tcg_rn; + DisasCompare c; if (!extract32(insn, 29, 1)) { unallocated_encoding(s); @@ -3589,19 +3590,13 @@ static void disas_cc(DisasContext *s, uint32_t insn) rn = extract32(insn, 5, 5); nzcv = extract32(insn, 0, 4); - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - tcg_tmp = tcg_temp_new_i64(); - tcg_gen_movi_i64(tcg_tmp, nzcv << 28); - gen_set_nzcv(tcg_tmp); - tcg_temp_free_i64(tcg_tmp); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } - /* match, or condition is always */ + /* Set T0 = !COND. */ + tcg_t0 = tcg_temp_new_i32(); + arm_test_cc(&c, cond); + tcg_gen_setcondi_i32(tcg_invert_cond(c.cond), tcg_t0, c.value, 0); + arm_free_cc(&c); + + /* Load the arguments for the new comparison. */ if (is_imm) { tcg_y = new_tmp_a64(s); tcg_gen_movi_i64(tcg_y, y); @@ -3610,6 +3605,7 @@ static void disas_cc(DisasContext *s, uint32_t insn) } tcg_rn = cpu_reg(s, rn); + /* Set the flags for the new comparison. */ tcg_tmp = tcg_temp_new_i64(); if (op) { gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y); @@ -3618,9 +3614,55 @@ static void disas_cc(DisasContext *s, uint32_t insn) } tcg_temp_free_i64(tcg_tmp); - if (cond < 0x0e) { /* continue */ - gen_set_label(label_continue); + /* If COND was false, force the flags to #nzcv. Compute two masks + * to help with this: T1 = (COND ? 0 : -1), T2 = (COND ? -1 : 0). + * For tcg hosts that support ANDC, we can make do with just T1. + * In either case, allow the tcg optimizer to delete any unused mask. + */ + tcg_t1 = tcg_temp_new_i32(); + tcg_t2 = tcg_temp_new_i32(); + tcg_gen_neg_i32(tcg_t1, tcg_t0); + tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); + + if (nzcv & 8) { /* N */ + tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_NF, cpu_NF, tcg_t2); + } } + if (nzcv & 4) { /* Z */ + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_ZF, cpu_ZF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_ZF, cpu_ZF, tcg_t2); + } + } else { + tcg_gen_or_i32(cpu_ZF, cpu_ZF, tcg_t0); + } + if (nzcv & 2) { /* C */ + tcg_gen_or_i32(cpu_CF, cpu_CF, tcg_t0); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_CF, cpu_CF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_CF, cpu_CF, tcg_t2); + } + } + if (nzcv & 1) { /* V */ + tcg_gen_or_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); + } + } + tcg_temp_free_i32(tcg_t0); + tcg_temp_free_i32(tcg_t1); + tcg_temp_free_i32(tcg_t2); } /* C3.5.6 Conditional select From 6e061029d74455d83f6fa070ac33de7a356cf60d Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:48 +0100 Subject: [PATCH 08/24] target-arm: Implement fcsel with movcond Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-7-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 45 ++++++++++++++------------------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index b48cd97314..d17ca19f03 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -4205,20 +4205,6 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) } } -/* copy src FP register to dst FP register; type specifies single or double */ -static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src) -{ - if (type) { - TCGv_i64 v = read_fp_dreg(s, src); - write_fp_dreg(s, dst, v); - tcg_temp_free_i64(v); - } else { - TCGv_i32 v = read_fp_sreg(s, src); - write_fp_sreg(s, dst, v); - tcg_temp_free_i32(v); - } -} - /* C3.6.24 Floating point conditional select * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+------+-----+------+------+ @@ -4228,7 +4214,8 @@ static void gen_mov_fp2fp(DisasContext *s, int type, int dst, int src) static void disas_fp_csel(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, cond, rn, rd; - TCGLabel *label_continue = NULL; + TCGv_i64 t_true, t_false, t_zero; + DisasCompare64 c; mos = extract32(insn, 29, 3); type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ @@ -4246,21 +4233,23 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) return; } - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - gen_mov_fp2fp(s, type, rd, rm); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } + /* Zero extend sreg inputs to 64 bits now. */ + t_true = tcg_temp_new_i64(); + t_false = tcg_temp_new_i64(); + read_vec_element(s, t_true, rn, 0, type ? MO_64 : MO_32); + read_vec_element(s, t_false, rm, 0, type ? MO_64 : MO_32); - gen_mov_fp2fp(s, type, rd, rn); + a64_test_cc(&c, cond); + t_zero = tcg_const_i64(0); + tcg_gen_movcond_i64(c.cond, t_true, c.value, t_zero, t_true, t_false); + tcg_temp_free_i64(t_zero); + tcg_temp_free_i64(t_false); + a64_free_cc(&c); - if (cond < 0x0e) { /* continue */ - gen_set_label(label_continue); - } + /* Note that sregs write back zeros to the high bits, + and we've already done the zero-extension. */ + write_fp_dreg(s, rd, t_true); + tcg_temp_free_i64(t_true); } /* C3.6.25 Floating-point data-processing (1 source) - single precision */ From ef60151bee9a95e3a5cc98b345a19ed7eb435ddb Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:48 +0100 Subject: [PATCH 09/24] target-arm: Recognize SXTB, SXTH, SXTW, ASR These are all special case aliases of SBFM. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-8-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index d17ca19f03..8ae68148ef 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -3017,7 +3017,28 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) tcg_rd = cpu_reg(s, rd); tcg_tmp = read_cpu_reg(s, rn, sf); - /* OPTME: probably worth recognizing common cases of ext{8,16,32}{u,s} */ + /* Recognize the common aliases. */ + if (opc == 0) { /* SBFM */ + if (ri == 0) { + if (si == 7) { /* SXTB */ + tcg_gen_ext8s_i64(tcg_rd, tcg_tmp); + goto done; + } else if (si == 15) { /* SXTH */ + tcg_gen_ext16s_i64(tcg_rd, tcg_tmp); + goto done; + } else if (si == 31) { /* SXTW */ + tcg_gen_ext32s_i64(tcg_rd, tcg_tmp); + goto done; + } + } + if (si == 63 || (si == 31 && ri <= si)) { /* ASR */ + if (si == 31) { + tcg_gen_ext32s_i64(tcg_tmp, tcg_tmp); + } + tcg_gen_sari_i64(tcg_rd, tcg_tmp, ri); + goto done; + } + } if (opc != 1) { /* SBFM or UBFM */ tcg_gen_movi_i64(tcg_rd, 0); @@ -3042,6 +3063,7 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) tcg_gen_sari_i64(tcg_rd, tcg_rd, 64 - (pos + len)); } + done: if (!sf) { /* zero extend final result */ tcg_gen_ext32u_i64(tcg_rd, tcg_rd); } From 9924e85829fe21b5f38a5d267c9aea44c5d478ac Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:48 +0100 Subject: [PATCH 10/24] target-arm: Recognize UXTB, UXTH, LSR, LSL These are all special case aliases of UBFM. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-9-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 8ae68148ef..f2f8443483 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -3038,6 +3038,23 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) tcg_gen_sari_i64(tcg_rd, tcg_tmp, ri); goto done; } + } else if (opc == 2) { /* UBFM */ + if (ri == 0) { /* UXTB, UXTH, plus non-canonical AND */ + tcg_gen_andi_i64(tcg_rd, tcg_tmp, bitmask64(si + 1)); + return; + } + if (si == 63 || (si == 31 && ri <= si)) { /* LSR */ + if (si == 31) { + tcg_gen_ext32u_i64(tcg_tmp, tcg_tmp); + } + tcg_gen_shri_i64(tcg_rd, tcg_tmp, ri); + return; + } + if (si + 1 == ri && si != bitsize - 1) { /* LSL */ + int shift = bitsize - 1 - si; + tcg_gen_shli_i64(tcg_rd, tcg_tmp, shift); + goto done; + } } if (opc != 1) { /* SBFM or UBFM */ From d3a77b42decd0cbfa62a5526e67d1d6d380c83a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:48 +0100 Subject: [PATCH 11/24] target-arm: Eliminate unnecessary zero-extend in disas_bitfield For !SF, this initial ext32u can't be optimized away by the current TCG code generator. (It would require backward bit liveness propagation.) But since the range of bits for !SF are already constrained by unallocated_encoding, we'll never reference the high bits anyway. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-10-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index f2f8443483..3ab0b42258 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -3015,7 +3015,11 @@ static void disas_bitfield(DisasContext *s, uint32_t insn) } tcg_rd = cpu_reg(s, rd); - tcg_tmp = read_cpu_reg(s, rn, sf); + + /* Suppress the zero-extend for !sf. Since RI and SI are constrained + to be smaller than bitsize, we'll never reference data outside the + low 32-bits anyway. */ + tcg_tmp = read_cpu_reg(s, rn, 1); /* Recognize the common aliases. */ if (opc == 0) { /* SBFM */ From 8fb0ad8e16ab3d03433244a1a03e1df757342ad8 Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 12/24] target-arm: Recognize ROR Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-11-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 3ab0b42258..04e7479625 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -3117,17 +3117,7 @@ static void disas_extract(DisasContext *s, uint32_t insn) tcg_rd = cpu_reg(s, rd); - if (imm) { - /* OPTME: we can special case rm==rn as a rotate */ - tcg_rm = read_cpu_reg(s, rm, sf); - tcg_rn = read_cpu_reg(s, rn, sf); - tcg_gen_shri_i64(tcg_rm, tcg_rm, imm); - tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm); - tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn); - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - } else { + if (unlikely(imm == 0)) { /* tcg shl_i32/shl_i64 is undefined for 32/64 bit shifts, * so an extract from bit 0 is a special case. */ @@ -3136,8 +3126,27 @@ static void disas_extract(DisasContext *s, uint32_t insn) } else { tcg_gen_ext32u_i64(tcg_rd, cpu_reg(s, rm)); } + } else if (rm == rn) { /* ROR */ + tcg_rm = cpu_reg(s, rm); + if (sf) { + tcg_gen_rotri_i64(tcg_rd, tcg_rm, imm); + } else { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, tcg_rm); + tcg_gen_rotri_i32(tmp, tmp, imm); + tcg_gen_extu_i32_i64(tcg_rd, tmp); + tcg_temp_free_i32(tmp); + } + } else { + tcg_rm = read_cpu_reg(s, rm, sf); + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_gen_shri_i64(tcg_rm, tcg_rm, imm); + tcg_gen_shli_i64(tcg_rn, tcg_rn, bitsize - imm); + tcg_gen_or_i64(tcg_rd, tcg_rm, tcg_rn); + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } } - } } From 7cb36e18b2f1c1f971ebdc2121de22a8c2e94fd6 Mon Sep 17 00:00:00 2001 From: Richard Henderson <rth@twiddle.net> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 13/24] target-arm: Use tcg_gen_extrh_i64_i32 Usually, eliminate an operation from the translator by combining a shift with an extract. In the case of gen_set_NZ64, we don't need a boolean value for cpu_ZF, merely a non-zero value. Given that we can extract both halves of a 64-bit input in one call, this simplifies the code. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <rth@twiddle.net> Message-id: 1441909103-24666-12-git-send-email-rth@twiddle.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/translate-a64.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index 04e7479625..ec0936cf97 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -529,13 +529,8 @@ static TCGv_ptr get_fpstatus_ptr(void) */ static inline void gen_set_NZ64(TCGv_i64 result) { - TCGv_i64 flag = tcg_temp_new_i64(); - - tcg_gen_setcondi_i64(TCG_COND_NE, flag, result, 0); - tcg_gen_extrl_i64_i32(cpu_ZF, flag); - tcg_gen_shri_i64(flag, result, 32); - tcg_gen_extrl_i64_i32(cpu_NF, flag); - tcg_temp_free_i64(flag); + tcg_gen_extr_i64_i32(cpu_ZF, cpu_NF, result); + tcg_gen_or_i32(cpu_ZF, cpu_ZF, cpu_NF); } /* Set NZCV as for a logical operation: NZ as per result, CV cleared. */ @@ -545,7 +540,7 @@ static inline void gen_logic_CC(int sf, TCGv_i64 result) gen_set_NZ64(result); } else { tcg_gen_extrl_i64_i32(cpu_ZF, result); - tcg_gen_extrl_i64_i32(cpu_NF, result); + tcg_gen_mov_i32(cpu_NF, cpu_ZF); } tcg_gen_movi_i32(cpu_CF, 0); tcg_gen_movi_i32(cpu_VF, 0); @@ -571,8 +566,7 @@ static void gen_add_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_andc_i64(flag, flag, tmp); tcg_temp_free_i64(tmp); - tcg_gen_shri_i64(flag, flag, 32); - tcg_gen_extrl_i64_i32(cpu_VF, flag); + tcg_gen_extrh_i64_i32(cpu_VF, flag); tcg_gen_mov_i64(dest, result); tcg_temp_free_i64(result); @@ -620,8 +614,7 @@ static void gen_sub_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_and_i64(flag, flag, tmp); tcg_temp_free_i64(tmp); - tcg_gen_shri_i64(flag, flag, 32); - tcg_gen_extrl_i64_i32(cpu_VF, flag); + tcg_gen_extrh_i64_i32(cpu_VF, flag); tcg_gen_mov_i64(dest, result); tcg_temp_free_i64(flag); tcg_temp_free_i64(result); @@ -680,8 +673,7 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) tcg_gen_xor_i64(vf_64, result, t0); tcg_gen_xor_i64(tmp, t0, t1); tcg_gen_andc_i64(vf_64, vf_64, tmp); - tcg_gen_shri_i64(vf_64, vf_64, 32); - tcg_gen_extrl_i64_i32(cpu_VF, vf_64); + tcg_gen_extrh_i64_i32(cpu_VF, vf_64); tcg_gen_mov_i64(dest, result); @@ -7775,10 +7767,8 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, } else { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tcg_lo, tcg_op); + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, cpu_env); - tcg_gen_shri_i64(tcg_op, tcg_op, 32); - tcg_gen_extrl_i64_i32(tcg_hi, tcg_op); gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, cpu_env); tcg_gen_deposit_i32(tcg_res[pass], tcg_lo, tcg_hi, 16, 16); tcg_temp_free_i32(tcg_lo); @@ -8684,16 +8674,10 @@ static void handle_3rd_wide(DisasContext *s, int is_q, int is_u, int size, } } -static void do_narrow_high_u32(TCGv_i32 res, TCGv_i64 in) -{ - tcg_gen_shri_i64(in, in, 32); - tcg_gen_extrl_i64_i32(res, in); -} - static void do_narrow_round_high_u32(TCGv_i32 res, TCGv_i64 in) { tcg_gen_addi_i64(in, in, 1U << 31); - do_narrow_high_u32(res, in); + tcg_gen_extrh_i64_i32(res, in); } static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, @@ -8712,7 +8696,7 @@ static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, gen_helper_neon_narrow_round_high_u8 }, { gen_helper_neon_narrow_high_u16, gen_helper_neon_narrow_round_high_u16 }, - { do_narrow_high_u32, do_narrow_round_high_u32 }, + { tcg_gen_extrh_i64_i32, do_narrow_round_high_u32 }, }; NeonGenNarrowFn *gennarrow = narrowfns[size][is_u]; From f4427280977902273f98280b2572d88b6ed53144 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois <jcd@tribudubois.net> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 14/24] i.MX: Add GPIO device Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com> Message-id: 5ea3b0021e47cf7f7d883a7edbabee44980f3df7.1441828793.git.jcd@tribudubois.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/gpio/Makefile.objs | 1 + hw/gpio/imx_gpio.c | 340 +++++++++++++++++++++++++++++++++++++ include/hw/gpio/imx_gpio.h | 62 +++++++ 3 files changed, 403 insertions(+) create mode 100644 hw/gpio/imx_gpio.c create mode 100644 include/hw/gpio/imx_gpio.h diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index 1abcf17988..52233f7e2f 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o common-obj-$(CONFIG_E500) += mpc8xxx.o obj-$(CONFIG_OMAP) += omap_gpio.o +obj-$(CONFIG_IMX) += imx_gpio.o diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c new file mode 100644 index 0000000000..d56ffcd8d7 --- /dev/null +++ b/hw/gpio/imx_gpio.c @@ -0,0 +1,340 @@ +/* + * i.MX processors GPIO emulation. + * + * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "hw/gpio/imx_gpio.h" + +#ifndef DEBUG_IMX_GPIO +#define DEBUG_IMX_GPIO 0 +#endif + +typedef enum IMXGPIOLevel { + IMX_GPIO_LEVEL_LOW = 0, + IMX_GPIO_LEVEL_HIGH = 1, +} IMXGPIOLevel; + +#define DPRINTF(fmt, args...) \ + do { \ + if (DEBUG_IMX_GPIO) { \ + fprintf(stderr, "%s: " fmt , __func__, ##args); \ + } \ + } while (0) + +static const char *imx_gpio_reg_name(uint32_t reg) +{ + switch (reg) { + case DR_ADDR: + return "DR"; + case GDIR_ADDR: + return "GDIR"; + case PSR_ADDR: + return "PSR"; + case ICR1_ADDR: + return "ICR1"; + case ICR2_ADDR: + return "ICR2"; + case IMR_ADDR: + return "IMR"; + case ISR_ADDR: + return "ISR"; + case EDGE_SEL_ADDR: + return "EDGE_SEL"; + default: + return "[?]"; + } +} + +static void imx_gpio_update_int(IMXGPIOState *s) +{ + qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0); +} + +static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level) +{ + /* if this signal isn't configured as an input signal, nothing to do */ + if (!extract32(s->gdir, line, 1)) { + return; + } + + /* When set, EDGE_SEL overrides the ICR config */ + if (extract32(s->edge_sel, line, 1)) { + /* we detect interrupt on rising and falling edge */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + s->isr = deposit32(s->isr, line, 1, 1); + } + } else if (extract64(s->icr, 2*line + 1, 1)) { + /* interrupt is edge sensitive */ + if (extract32(s->psr, line, 1) != level) { + /* level changed */ + if (extract64(s->icr, 2*line, 1) != level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } + } else { + /* interrupt is level sensitive */ + if (extract64(s->icr, 2*line, 1) == level) { + s->isr = deposit32(s->isr, line, 1, 1); + } + } +} + +static void imx_gpio_set(void *opaque, int line, int level) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + IMXGPIOLevel imx_level = level ? IMX_GPIO_LEVEL_HIGH : IMX_GPIO_LEVEL_LOW; + + imx_gpio_set_int_line(s, line, imx_level); + + /* this is an input signal, so set PSR */ + s->psr = deposit32(s->psr, line, 1, imx_level); + + imx_gpio_update_int(s); +} + +static void imx_gpio_set_all_int_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + IMXGPIOLevel imx_level = extract32(s->psr, i, 1); + imx_gpio_set_int_line(s, i, imx_level); + } + + imx_gpio_update_int(s); +} + +static inline void imx_gpio_set_all_output_lines(IMXGPIOState *s) +{ + int i; + + for (i = 0; i < IMX_GPIO_PIN_COUNT; i++) { + /* + * if the line is set as output, then forward the line + * level to its user. + */ + if (extract32(s->gdir, i, 1) && s->output[i]) { + qemu_set_irq(s->output[i], extract32(s->dr, i, 1)); + } + } +} + +static uint64_t imx_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + uint32_t reg_value = 0; + + switch (offset) { + case DR_ADDR: + /* + * depending on the "line" configuration, the bit values + * are coming either from DR or PSR + */ + reg_value = (s->dr & s->gdir) | (s->psr & ~s->gdir); + break; + + case GDIR_ADDR: + reg_value = s->gdir; + break; + + case PSR_ADDR: + reg_value = s->psr & ~s->gdir; + break; + + case ICR1_ADDR: + reg_value = extract64(s->icr, 0, 32); + break; + + case ICR2_ADDR: + reg_value = extract64(s->icr, 32, 32); + break; + + case IMR_ADDR: + reg_value = s->imr; + break; + + case ISR_ADDR: + reg_value = s->isr; + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + reg_value = s->edge_sel; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", + TYPE_IMX_GPIO, __func__, (int)offset); + break; + } + + DPRINTF("(%s) = 0x%"PRIx32"\n", imx_gpio_reg_name(offset), reg_value); + + return reg_value; +} + +static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXGPIOState *s = IMX_GPIO(opaque); + + DPRINTF("(%s, value = 0x%"PRIx32")\n", imx_gpio_reg_name(offset), + (uint32_t)value); + + switch (offset) { + case DR_ADDR: + s->dr = value; + imx_gpio_set_all_output_lines(s); + break; + + case GDIR_ADDR: + s->gdir = value; + imx_gpio_set_all_output_lines(s); + imx_gpio_set_all_int_lines(s); + break; + + case ICR1_ADDR: + s->icr = deposit64(s->icr, 0, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case ICR2_ADDR: + s->icr = deposit64(s->icr, 32, 32, value); + imx_gpio_set_all_int_lines(s); + break; + + case IMR_ADDR: + s->imr = value; + imx_gpio_update_int(s); + break; + + case ISR_ADDR: + s->isr |= ~value; + imx_gpio_set_all_int_lines(s); + break; + + case EDGE_SEL_ADDR: + if (s->has_edge_sel) { + s->edge_sel = value; + imx_gpio_set_all_int_lines(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: EDGE_SEL register not " + "present on this version of GPIO device\n", + TYPE_IMX_GPIO, __func__); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad register at offset %d\n", + TYPE_IMX_GPIO, __func__, (int)offset); + break; + } + + return; +} + +static const MemoryRegionOps imx_gpio_ops = { + .read = imx_gpio_read, + .write = imx_gpio_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription vmstate_imx_gpio = { + .name = TYPE_IMX_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(dr, IMXGPIOState), + VMSTATE_UINT32(gdir, IMXGPIOState), + VMSTATE_UINT32(psr, IMXGPIOState), + VMSTATE_UINT64(icr, IMXGPIOState), + VMSTATE_UINT32(imr, IMXGPIOState), + VMSTATE_UINT32(isr, IMXGPIOState), + VMSTATE_BOOL(has_edge_sel, IMXGPIOState), + VMSTATE_UINT32(edge_sel, IMXGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static Property imx_gpio_properties[] = { + DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_gpio_reset(DeviceState *dev) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + s->dr = 0; + s->gdir = 0; + s->psr = 0; + s->icr = 0; + s->imr = 0; + s->isr = 0; + s->edge_sel = 0; + + imx_gpio_set_all_output_lines(s); + imx_gpio_update_int(s); +} + +static void imx_gpio_realize(DeviceState *dev, Error **errp) +{ + IMXGPIOState *s = IMX_GPIO(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpio_ops, s, + TYPE_IMX_GPIO, IMX_GPIO_MEM_SIZE); + + qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT); + qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT); + + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); +} + +static void imx_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = imx_gpio_realize; + dc->reset = imx_gpio_reset; + dc->props = imx_gpio_properties; + dc->vmsd = &vmstate_imx_gpio; + dc->desc = "i.MX GPIO controller"; +} + +static const TypeInfo imx_gpio_info = { + .name = TYPE_IMX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXGPIOState), + .class_init = imx_gpio_class_init, +}; + +static void imx_gpio_register_types(void) +{ + type_register_static(&imx_gpio_info); +} + +type_init(imx_gpio_register_types) diff --git a/include/hw/gpio/imx_gpio.h b/include/hw/gpio/imx_gpio.h new file mode 100644 index 0000000000..517b2618cb --- /dev/null +++ b/include/hw/gpio/imx_gpio.h @@ -0,0 +1,62 @@ +/* + * i.MX processors GPIO registers definition. + * + * Copyright (C) 2015 Jean-Christophe Dubois <jcd@tribudubois.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __IMX_GPIO_H_ +#define __IMX_GPIO_H_ + +#include <hw/sysbus.h> + +#define TYPE_IMX_GPIO "imx.gpio" +#define IMX_GPIO(obj) OBJECT_CHECK(IMXGPIOState, (obj), TYPE_IMX_GPIO) + +#define IMX_GPIO_MEM_SIZE 0x20 + +/* i.MX GPIO memory map */ +#define DR_ADDR 0x00 /* DATA REGISTER */ +#define GDIR_ADDR 0x04 /* DIRECTION REGISTER */ +#define PSR_ADDR 0x08 /* PAD STATUS REGISTER */ +#define ICR1_ADDR 0x0c /* INTERRUPT CONFIGURATION REGISTER 1 */ +#define ICR2_ADDR 0x10 /* INTERRUPT CONFIGURATION REGISTER 2 */ +#define IMR_ADDR 0x14 /* INTERRUPT MASK REGISTER */ +#define ISR_ADDR 0x18 /* INTERRUPT STATUS REGISTER */ +#define EDGE_SEL_ADDR 0x1c /* EDGE SEL REGISTER */ + +#define IMX_GPIO_PIN_COUNT 32 + +typedef struct IMXGPIOState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t dr; + uint32_t gdir; + uint32_t psr; + uint64_t icr; + uint32_t imr; + uint32_t isr; + bool has_edge_sel; + uint32_t edge_sel; + + qemu_irq irq; + qemu_irq output[IMX_GPIO_PIN_COUNT]; +} IMXGPIOState; + +#endif /* __IMX_GPIO_H_ */ From dde0c4ca6b849eb5c376f255767c019bb45a1d57 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois <jcd@tribudubois.net> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 15/24] i.MX: Add GPIO devices to i.MX31 SOC Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com> Message-id: 60b67c9a8b948159f4b4163ead86fbf701c011c6.1441828793.git.jcd@tribudubois.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx31.c | 30 ++++++++++++++++++++++++++++++ include/hw/arm/fsl-imx31.h | 12 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 87548c8352..8e1ed4811b 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -55,6 +55,11 @@ static void fsl_imx31_init(Object *obj) object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); } + + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } } static void fsl_imx31_realize(DeviceState *dev, Error **errp) @@ -184,6 +189,31 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) i2c_table[i].irq)); } + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX31_NUM_GPIOS] = { + { FSL_IMX31_GPIO1_ADDR, FSL_IMX31_GPIO1_IRQ }, + { FSL_IMX31_GPIO2_ADDR, FSL_IMX31_GPIO2_IRQ }, + { FSL_IMX31_GPIO3_ADDR, FSL_IMX31_GPIO3_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), false, "has-edge-sel", + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + /* On a real system, the first 16k is a `secure boot rom' */ memory_region_init_rom_device(&s->secure_rom, NULL, NULL, NULL, "imx31.secure_rom", diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index 891166f2cc..5e8f795f98 100644 --- a/include/hw/arm/fsl-imx31.h +++ b/include/hw/arm/fsl-imx31.h @@ -24,6 +24,7 @@ #include "hw/timer/imx_gpt.h" #include "hw/timer/imx_epit.h" #include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" #include "exec/memory.h" #define TYPE_FSL_IMX31 "fsl,imx31" @@ -32,6 +33,7 @@ #define FSL_IMX31_NUM_UARTS 2 #define FSL_IMX31_NUM_EPITS 2 #define FSL_IMX31_NUM_I2CS 3 +#define FSL_IMX31_NUM_GPIOS 3 typedef struct FslIMX31State { /*< private >*/ @@ -45,6 +47,7 @@ typedef struct FslIMX31State { IMXGPTState gpt; IMXEPITState epit[FSL_IMX31_NUM_EPITS]; IMXI2CState i2c[FSL_IMX31_NUM_I2CS]; + IMXGPIOState gpio[FSL_IMX31_NUM_GPIOS]; MemoryRegion secure_rom; MemoryRegion rom; MemoryRegion iram; @@ -77,6 +80,12 @@ typedef struct FslIMX31State { #define FSL_IMX31_EPIT1_SIZE 0x4000 #define FSL_IMX31_EPIT2_ADDR 0x53F98000 #define FSL_IMX31_EPIT2_SIZE 0x4000 +#define FSL_IMX31_GPIO3_ADDR 0x53FA4000 +#define FSL_IMX31_GPIO3_SIZE 0x4000 +#define FSL_IMX31_GPIO1_ADDR 0x53FCC000 +#define FSL_IMX31_GPIO1_SIZE 0x4000 +#define FSL_IMX31_GPIO2_ADDR 0x53FD0000 +#define FSL_IMX31_GPIO2_SIZE 0x4000 #define FSL_IMX31_AVIC_ADDR 0x68000000 #define FSL_IMX31_AVIC_SIZE 0x100 #define FSL_IMX31_SDRAM0_ADDR 0x80000000 @@ -106,5 +115,8 @@ typedef struct FslIMX31State { #define FSL_IMX31_I2C1_IRQ 10 #define FSL_IMX31_I2C2_IRQ 4 #define FSL_IMX31_I2C3_IRQ 3 +#define FSL_IMX31_GPIO1_IRQ 52 +#define FSL_IMX31_GPIO2_IRQ 51 +#define FSL_IMX31_GPIO3_IRQ 56 #endif /* FSL_IMX31_H */ From 6abc7158cb62e559ce7b3a99e116e3ec051a0c45 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois <jcd@tribudubois.net> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 16/24] i.MX: Add GPIO devices to i.MX25 SOC Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net> Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com> Message-id: 2eb129ba8713aedfe877eaa3d8de80061d880fbb.1441828793.git.jcd@tribudubois.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/fsl-imx25.c | 29 +++++++++++++++++++++++++++++ include/hw/arm/fsl-imx25.h | 15 +++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 6d157c9486..86fde42e34 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -63,6 +63,11 @@ static void fsl_imx25_init(Object *obj) object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); } + + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); + qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + } } static void fsl_imx25_realize(DeviceState *dev, Error **errp) @@ -214,6 +219,30 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) i2c_table[i].irq)); } + /* Initialize all GPIOs */ + for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[FSL_IMX25_NUM_GPIOS] = { + { FSL_IMX25_GPIO1_ADDR, FSL_IMX25_GPIO1_IRQ }, + { FSL_IMX25_GPIO2_ADDR, FSL_IMX25_GPIO2_IRQ }, + { FSL_IMX25_GPIO3_ADDR, FSL_IMX25_GPIO3_IRQ }, + { FSL_IMX25_GPIO4_ADDR, FSL_IMX25_GPIO4_IRQ } + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + /* Connect GPIO IRQ to PIC */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + gpio_table[i].irq)); + } + /* initialize 2 x 16 KB ROM */ memory_region_init_rom_device(&s->rom[0], NULL, NULL, NULL, "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 7f6bb64e7d..73f50c64d8 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -25,6 +25,7 @@ #include "hw/timer/imx_epit.h" #include "hw/net/imx_fec.h" #include "hw/i2c/imx_i2c.h" +#include "hw/gpio/imx_gpio.h" #include "exec/memory.h" #define TYPE_FSL_IMX25 "fsl,imx25" @@ -34,6 +35,7 @@ #define FSL_IMX25_NUM_GPTS 4 #define FSL_IMX25_NUM_EPITS 2 #define FSL_IMX25_NUM_I2CS 3 +#define FSL_IMX25_NUM_GPIOS 4 typedef struct FslIMX25State { /*< private >*/ @@ -48,6 +50,7 @@ typedef struct FslIMX25State { IMXEPITState epit[FSL_IMX25_NUM_EPITS]; IMXFECState fec; IMXI2CState i2c[FSL_IMX25_NUM_I2CS]; + IMXGPIOState gpio[FSL_IMX25_NUM_GPIOS]; MemoryRegion rom[2]; MemoryRegion iram; MemoryRegion iram_alias; @@ -204,6 +207,14 @@ typedef struct FslIMX25State { #define FSL_IMX25_EPIT1_SIZE 0x4000 #define FSL_IMX25_EPIT2_ADDR 0x53F98000 #define FSL_IMX25_EPIT2_SIZE 0x4000 +#define FSL_IMX25_GPIO4_ADDR 0x53F9C000 +#define FSL_IMX25_GPIO4_SIZE 0x4000 +#define FSL_IMX25_GPIO3_ADDR 0x53FA4000 +#define FSL_IMX25_GPIO3_SIZE 0x4000 +#define FSL_IMX25_GPIO1_ADDR 0x53FCC000 +#define FSL_IMX25_GPIO1_SIZE 0x4000 +#define FSL_IMX25_GPIO2_ADDR 0x53FD0000 +#define FSL_IMX25_GPIO2_SIZE 0x4000 #define FSL_IMX25_AVIC_ADDR 0x68000000 #define FSL_IMX25_AVIC_SIZE 0x4000 #define FSL_IMX25_IRAM_ADDR 0x78000000 @@ -230,5 +241,9 @@ typedef struct FslIMX25State { #define FSL_IMX25_I2C1_IRQ 3 #define FSL_IMX25_I2C2_IRQ 4 #define FSL_IMX25_I2C3_IRQ 10 +#define FSL_IMX25_GPIO1_IRQ 52 +#define FSL_IMX25_GPIO2_IRQ 51 +#define FSL_IMX25_GPIO3_IRQ 16 +#define FSL_IMX25_GPIO4_IRQ 23 #endif /* FSL_IMX25_H */ From 6533a1fcc2efa08570aa6d85851638783dddf2c6 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:49 +0100 Subject: [PATCH 17/24] hw/cpu/{a15mpcore, a9mpcore}: Handle missing has_el3 CPU props gracefully Handle missing CPU support for EL3 gracefully. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-2-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- hw/cpu/a15mpcore.c | 2 +- hw/cpu/a9mpcore.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 4ef8db1a4c..94e8cc1a66 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -64,7 +64,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) * either all the CPUs have TZ, or none do. */ cpuobj = OBJECT(qemu_get_cpu(0)); - has_el3 = object_property_find(cpuobj, "has_el3", &error_abort) && + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && object_property_get_bool(cpuobj, "has_el3", &error_abort); qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); } diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 7046246896..869818cd8a 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -69,7 +69,7 @@ static void a9mp_priv_realize(DeviceState *dev, Error **errp) * either all the CPUs have TZ, or none do. */ cpuobj = OBJECT(qemu_get_cpu(0)); - has_el3 = object_property_find(cpuobj, "has_el3", &error_abort) && + has_el3 = object_property_find(cpuobj, "has_el3", NULL) && object_property_get_bool(cpuobj, "has_el3", &error_abort); qdev_prop_set_bit(gicdev, "has-security-extensions", has_el3); From 68e9c2fe65bca7fc1bdc2411923333c3e87544a3 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:50 +0100 Subject: [PATCH 18/24] target-arm: Add VTCR_EL2 Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-3-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: fixed typo in comment] Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu.h | 1 + target-arm/helper.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index b9068c9dad..f91b7933b6 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -224,6 +224,7 @@ typedef struct CPUARMState { }; /* MMU translation table base control. */ TCR tcr_el[4]; + TCR vtcr_el2; /* Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ uint32_t c2_insn; /* MPU instruction cacheable bits. */ union { /* MMU domain access control register diff --git a/target-arm/helper.c b/target-arm/helper.c index 2c6ec9d6cf..b250ed0e61 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -325,6 +325,34 @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } +/* + * Some registers are not accessible if EL3.NS=0 and EL3 is using AArch32 but + * they are accessible when EL3 is using AArch64 regardless of EL3.NS. + * + * access_el3_aa32ns: Used to check AArch32 register views. + * access_el3_aa32ns_aa64any: Used to check both AArch32/64 register views. + */ +static CPAccessResult access_el3_aa32ns(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + bool secure = arm_is_secure_below_el3(env); + + assert(!arm_el_is_aa64(env, 3)); + if (secure) { + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_el3_aa32ns_aa64any(CPUARMState *env, + const ARMCPRegInfo *ri) +{ + if (!arm_el_is_aa64(env, 3)) { + return access_el3_aa32ns(env, ri); + } + return CP_ACCESS_OK; +} + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -3112,6 +3140,10 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "TCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 0, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "VTCR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3246,6 +3278,14 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .writefn = vmsa_tcr_el1_write, .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[2]) }, + { .name = "VTCR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL2_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write, @@ -5741,8 +5781,7 @@ static inline bool regime_translation_disabled(CPUARMState *env, static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { if (mmu_idx == ARMMMUIdx_S2NS) { - /* TODO: return VTCR_EL2 */ - g_assert_not_reached(); + return &env->cp15.vtcr_el2; } return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; } From b698e9cfd282b228b36d426b75facb83e07a1072 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:50 +0100 Subject: [PATCH 19/24] target-arm: Add VTTBR_EL2 Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-4-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu.h | 1 + target-arm/helper.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index f91b7933b6..96b8f2afba 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -222,6 +222,7 @@ typedef struct CPUARMState { }; uint64_t ttbr1_el[4]; }; + uint64_t vttbr_el2; /* Virtualization Translation Table Base. */ /* MMU translation table base control. */ TCR tcr_el[4]; TCR vtcr_el2; /* Virtualization Translation Control. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index b250ed0e61..d84f3c94f7 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2213,6 +2213,20 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } +static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + /* Accesses to VTTBR may change the VMID so we must flush the TLB. */ + if (raw_read(env, ri) != value) { + tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, + ARMMMUIdx_S2NS, -1); + raw_write(env, ri, value); + } +} + static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_ALIAS, @@ -3144,6 +3158,13 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "VTTBR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 6, .crm = 2, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .type = ARM_CP_CONST | ARM_CP_64BIT, .resetvalue = 0 }, + { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3286,6 +3307,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.vtcr_el2) }, + { .name = "VTTBR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 6, .crm = 2, + .type = ARM_CP_64BIT | ARM_CP_ALIAS, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2), + .writefn = vttbr_write }, + { .name = "VTTBR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 0, + .access = PL2_RW, .writefn = vttbr_write, + .fieldoffset = offsetof(CPUARMState, cp15.vttbr_el2) }, { .name = "SCTLR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 0, .access = PL2_RW, .raw_writefn = raw_write, .writefn = sctlr_write, @@ -5791,8 +5822,7 @@ static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, int ttbrn) { if (mmu_idx == ARMMMUIdx_S2NS) { - /* TODO: return VTTBR_EL2 */ - g_assert_not_reached(); + return env->cp15.vttbr_el2; } if (ttbrn == 0) { return env->cp15.ttbr0_el[regime_el(env, mmu_idx)]; From 1edee4708a0e3163cbf20fac325be456abd960bb Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:50 +0100 Subject: [PATCH 20/24] target-arm: Suppress TBI for S2 translations Stage-2 MMU translations do not have configurable TBI as the top byte is always 0 (48-bit IPAs). Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-5-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index d84f3c94f7..200b9f2e9f 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -6370,7 +6370,9 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, if (arm_el_is_aa64(env, el)) { va_size = 64; if (el > 1) { - tbi = extract64(tcr->raw_tcr, 20, 1); + if (mmu_idx != ARMMMUIdx_S2NS) { + tbi = extract64(tcr->raw_tcr, 20, 1); + } } else { if (extract64(address, 55, 1)) { tbi = extract64(tcr->raw_tcr, 38, 1); From 0c5fbf3b4c1e5210354de71a3dc2ebc8c8a01f31 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:50 +0100 Subject: [PATCH 21/24] target-arm: Suppress EPD for S2, EL2 and EL3 translations Stage-2 translations, EL2 and EL3 regimes don't have the EPD control. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-6-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 200b9f2e9f..478347b38b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -6344,7 +6344,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Read an LPAE long-descriptor translation table. */ MMUFaultType fault_type = translation_fault; uint32_t level = 1; - uint32_t epd; + uint32_t epd = 0; int32_t tsz; uint32_t tg; uint64_t ttbr; @@ -6438,7 +6438,9 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, */ if (ttbr_select == 0) { ttbr = regime_ttbr(env, mmu_idx, 0); - epd = extract32(tcr->raw_tcr, 7, 1); + if (el < 2) { + epd = extract32(tcr->raw_tcr, 7, 1); + } tsz = t0sz; tg = extract32(tcr->raw_tcr, 14, 2); From 731de9e60074620aa7d565f01f989adacd493514 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:50 +0100 Subject: [PATCH 22/24] target-arm: Add VPIDR_EL2 Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-7-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu.h | 1 + target-arm/helper.c | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 96b8f2afba..a7d577bd89 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -385,6 +385,7 @@ typedef struct CPUARMState { */ uint64_t c15_ccnt; uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */ + uint64_t vpidr_el2; /* Virtualization Processor ID Register */ } cp15; struct { diff --git a/target-arm/helper.c b/target-arm/helper.c index 478347b38b..d18106c277 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2445,6 +2445,18 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { REGINFO_SENTINEL }; +static uint64_t midr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + + if (arm_feature(&cpu->env, ARM_FEATURE_EL2) && !secure && cur_el == 1) { + return env->cp15.vpidr_el2; + } + return raw_read(env, ri); +} + static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env)); @@ -4121,6 +4133,19 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { + ARMCPRegInfo vpidr_regs[] = { + { .name = "VPIDR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VPIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { @@ -4136,6 +4161,18 @@ void register_cp_regs_for_features(ARMCPU *cpu) * register the no_el2 reginfos. */ if (arm_feature(env, ARM_FEATURE_EL3)) { + /* When EL3 exists but not EL2, VPIDR takes the value + * of MIDR_EL1. + */ + ARMCPRegInfo vpidr_regs[] = { + { .name = "VPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_CONST, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); } } @@ -4213,6 +4250,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_R, .resetvalue = cpu->midr, .writefn = arm_cp_write_ignore, .raw_writefn = raw_write, + .readfn = midr_read, .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), .type = ARM_CP_OVERRIDE }, /* crn = 0 op1 = 0 crm = 3..7 : currently unassigned; we RAZ. */ @@ -4236,7 +4274,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo id_v8_midr_cp_reginfo[] = { { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + .access = PL1_R, .type = ARM_CP_NO_RAW, .resetvalue = cpu->midr, + .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), + .readfn = midr_read }, /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */ { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, From 06a7e6477c129ceaa72bd400cf281d44c456be43 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:51 +0100 Subject: [PATCH 23/24] target-arm: Break out mpidr_read_val() Break out mpidr_read_val() to allow future sharing of the code that conditionally sets the M and U bits of MPIDR. No functional changes. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-8-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index d18106c277..86900c23f9 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2457,7 +2457,7 @@ static uint64_t midr_read(CPUARMState *env, const ARMCPRegInfo *ri) return raw_read(env, ri); } -static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) +static uint64_t mpidr_read_val(CPUARMState *env) { ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env)); uint64_t mpidr = cpu->mp_affinity; @@ -2475,6 +2475,11 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) return mpidr; } +static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return mpidr_read_val(env); +} + static const ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, From f0d574d63f4603ec431f16ad535a555bf7548b94 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" <edgar.iglesias@xilinx.com> Date: Mon, 14 Sep 2015 14:39:51 +0100 Subject: [PATCH 24/24] target-arm: Add VMPIDR_EL2 Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Message-id: 1442135278-25281-9-git-send-email-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> --- target-arm/cpu.h | 1 + target-arm/helper.c | 26 ++++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index a7d577bd89..1b8051613f 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -386,6 +386,7 @@ typedef struct CPUARMState { uint64_t c15_ccnt; uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */ uint64_t vpidr_el2; /* Virtualization Processor ID Register */ + uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */ } cp15; struct { diff --git a/target-arm/helper.c b/target-arm/helper.c index 86900c23f9..65b9ff5ad0 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2477,6 +2477,12 @@ static uint64_t mpidr_read_val(CPUARMState *env) static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { + unsigned int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + + if (arm_feature(env, ARM_FEATURE_EL2) && !secure && cur_el == 1) { + return env->cp15.vmpidr_el2; + } return mpidr_read_val(env); } @@ -4138,6 +4144,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { + uint64_t vmpidr_def = mpidr_read_val(env); ARMCPRegInfo vpidr_regs[] = { { .name = "VPIDR", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, @@ -4148,6 +4155,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 0, .access = PL2_RW, .resetvalue = cpu->midr, .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VMPIDR", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, .accessfn = access_el3_aa32ns, + .resetvalue = vmpidr_def, + .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, + { .name = "VMPIDR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, + .resetvalue = vmpidr_def, + .fieldoffset = offsetof(CPUARMState, cp15.vmpidr_el2) }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, vpidr_regs); @@ -4166,8 +4183,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) * register the no_el2 reginfos. */ if (arm_feature(env, ARM_FEATURE_EL3)) { - /* When EL3 exists but not EL2, VPIDR takes the value - * of MIDR_EL1. + /* When EL3 exists but not EL2, VPIDR and VMPIDR take the value + * of MIDR_EL1 and MPIDR_EL1. */ ARMCPRegInfo vpidr_regs[] = { { .name = "VPIDR_EL2", .state = ARM_CP_STATE_BOTH, @@ -4175,6 +4192,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, .type = ARM_CP_CONST, .resetvalue = cpu->midr, .fieldoffset = offsetof(CPUARMState, cp15.vpidr_el2) }, + { .name = "VMPIDR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 0, .crm = 0, .opc2 = 5, + .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, + .type = ARM_CP_NO_RAW, + .writefn = arm_cp_write_ignore, .readfn = mpidr_read }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, vpidr_regs);