From 81e69fb093099ec5dccd61c92cec308f83091511 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Mon, 15 Jul 2013 14:35:25 +0100 Subject: [PATCH 1/8] target-arm: add feature flag for ARMv8 Signed-off-by: Mans Rullgard Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/cpu.c | 7 ++++++- target-arm/cpu.h | 1 + target-arm/translate.c | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/target-arm/cpu.c b/target-arm/cpu.c index be26acc38d..9f1696f933 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -157,6 +157,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) CPUARMState *env = &cpu->env; /* Some features automatically imply others: */ + if (arm_feature(env, ARM_FEATURE_V8)) { + set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_ARM_DIV); + set_feature(env, ARM_FEATURE_LPAE); + } if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); set_feature(env, ARM_FEATURE_THUMB2); @@ -744,7 +749,7 @@ static void pxa270c5_initfn(Object *obj) static void arm_any_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); set_feature(&cpu->env, ARM_FEATURE_NEON); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 1369604d65..c798b272a0 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -387,6 +387,7 @@ enum arm_features { ARM_FEATURE_MPIDR, /* has cp15 MPIDR */ ARM_FEATURE_PXN, /* has Privileged Execute Never bit */ ARM_FEATURE_LPAE, /* has Large Physical Address Extension */ + ARM_FEATURE_V8, }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/translate.c b/target-arm/translate.c index 9310c586de..0db1132a88 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -42,6 +42,7 @@ #define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K) #define ENABLE_ARCH_6T2 arm_feature(env, ARM_FEATURE_THUMB2) #define ENABLE_ARCH_7 arm_feature(env, ARM_FEATURE_V7) +#define ENABLE_ARCH_8 arm_feature(env, ARM_FEATURE_V8) #define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0) From 2359bf80c1c4e8ed1e7ddb03661fec6bace82a87 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Mon, 15 Jul 2013 14:35:25 +0100 Subject: [PATCH 2/8] target-arm: implement LDA/STL instructions This adds support for the ARMv8 load acquire/store release instructions. Since qemu does nothing special for memory barriers, these can be emulated like their non-acquire/release counterparts. Signed-off-by: Mans Rullgard Signed-off-by: Peter Maydell --- target-arm/translate.c | 129 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 10 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 0db1132a88..b7663dd3d8 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7274,14 +7274,72 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) rd = (insn >> 12) & 0xf; if (insn & (1 << 23)) { /* load/store exclusive */ + int op2 = (insn >> 8) & 3; op1 = (insn >> 21) & 0x3; - if (op1) - ARCH(6K); - else - ARCH(6); + + switch (op2) { + case 0: /* lda/stl */ + if (op1 == 1) { + goto illegal_op; + } + ARCH(8); + break; + case 1: /* reserved */ + goto illegal_op; + case 2: /* ldaex/stlex */ + ARCH(8); + break; + case 3: /* ldrex/strex */ + if (op1) { + ARCH(6K); + } else { + ARCH(6); + } + break; + } + addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); - if (insn & (1 << 20)) { + + /* Since the emulation does not have barriers, + the acquire/release semantics need no special + handling */ + if (op2 == 0) { + if (insn & (1 << 20)) { + tmp = tcg_temp_new_i32(); + switch (op1) { + case 0: /* lda */ + tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + break; + case 2: /* ldab */ + tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + break; + case 3: /* ldah */ + tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + break; + default: + abort(); + } + store_reg(s, rd, tmp); + } else { + rm = insn & 0xf; + tmp = load_reg(s, rm); + switch (op1) { + case 0: /* stl */ + tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + break; + case 2: /* stlb */ + tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + break; + case 3: /* stlh */ + tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + break; + default: + abort(); + } + tcg_temp_free_i32(tmp); + } + } else if (insn & (1 << 20)) { switch (op1) { case 0: /* ldrex */ gen_load_exclusive(s, rd, 15, addr, 2); @@ -8126,7 +8184,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw gen_store_exclusive(s, rd, rs, 15, addr, 2); } tcg_temp_free_i32(addr); - } else if ((insn & (1 << 6)) == 0) { + } else if ((insn & (7 << 5)) == 0) { /* Table Branch. */ if (rn == 15) { addr = tcg_temp_new_i32(); @@ -8152,15 +8210,66 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tcg_gen_addi_i32(tmp, tmp, s->pc); store_reg(s, 15, tmp); } else { - /* Load/store exclusive byte/halfword/doubleword. */ - ARCH(7); + int op2 = (insn >> 6) & 0x3; op = (insn >> 4) & 0x3; - if (op == 2) { + switch (op2) { + case 0: goto illegal_op; + case 1: + /* Load/store exclusive byte/halfword/doubleword */ + if (op == 2) { + goto illegal_op; + } + ARCH(7); + break; + case 2: + /* Load-acquire/store-release */ + if (op == 3) { + goto illegal_op; + } + /* Fall through */ + case 3: + /* Load-acquire/store-release exclusive */ + ARCH(8); + break; } addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); - if (insn & (1 << 20)) { + if (!(op2 & 1)) { + if (insn & (1 << 20)) { + tmp = tcg_temp_new_i32(); + switch (op) { + case 0: /* ldab */ + tcg_gen_qemu_ld8u(tmp, addr, IS_USER(s)); + break; + case 1: /* ldah */ + tcg_gen_qemu_ld16u(tmp, addr, IS_USER(s)); + break; + case 2: /* lda */ + tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s)); + break; + default: + abort(); + } + store_reg(s, rs, tmp); + } else { + tmp = load_reg(s, rs); + switch (op) { + case 0: /* stlb */ + tcg_gen_qemu_st8(tmp, addr, IS_USER(s)); + break; + case 1: /* stlh */ + tcg_gen_qemu_st16(tmp, addr, IS_USER(s)); + break; + case 2: /* stl */ + tcg_gen_qemu_st32(tmp, addr, IS_USER(s)); + break; + default: + abort(); + } + tcg_temp_free_i32(tmp); + } + } else if (insn & (1 << 20)) { gen_load_exclusive(s, rs, rd, addr, op); } else { gen_store_exclusive(s, rm, rs, rd, addr, op); From 12b10571141a20e1c23d3b597e55d5d1a3113265 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Mon, 15 Jul 2013 14:35:25 +0100 Subject: [PATCH 3/8] target-arm: explicitly decode SEVL instruction The ARMv8 SEVL instruction is in the architectural hint space already emulated as nop. This makes the decoding of SEVL explicit for clarity. Signed-off-by: Mans Rullgard Message-id: 1370606786-5650-3-git-send-email-mans@mansr.com [PMM: added 'SEVL' to the TODO comment] Signed-off-by: Peter Maydell --- target-arm/translate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index b7663dd3d8..7b50c8c308 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -3501,7 +3501,8 @@ static void gen_nop_hint(DisasContext *s, int val) break; case 2: /* wfe */ case 4: /* sev */ - /* TODO: Implement SEV and WFE. May help SMP performance. */ + case 5: /* sevl */ + /* TODO: Implement SEV, SEVL and WFE. May help SMP performance. */ default: /* nop */ break; } From a703eda18a5c3e9b45f2b9a337a3e1e16c836cf9 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Wed, 10 Jul 2013 14:21:42 +1000 Subject: [PATCH 4/8] target-arm/helper.c: OMAP/StrongARM cp15 crn=0 cleanup The if block detecting OMAP/StrongARM modifies the id_cp_reginfo .access fields in place. So there is no need to replicate the call to define_arm_cp_reg(). Dropped, and let the OMAP case fall through to the normal behaviour after the in-place modification. Signed-off-by: Peter Crosthwaite Message-id: 72aae9b8ebbc9a76d2b06faf8666ef8a4b34b92a.1373429432.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- target-arm/helper.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 5f639fdeb8..47e6c09387 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1435,21 +1435,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) arm_feature(env, ARM_FEATURE_STRONGARM)) { ARMCPRegInfo *r; /* Register the blanket "writes ignored" value first to cover the - * whole space. Then define the specific ID registers, but update - * their access field to allow write access, so that they ignore - * writes rather than causing them to UNDEF. + * whole space. Then update the specific ID registers to allow write + * access, so that they ignore writes rather than causing them to + * UNDEF. */ define_one_arm_cp_reg(cpu, &crn0_wi_reginfo); for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { r->access = PL1_RW; - define_one_arm_cp_reg(cpu, r); } - } else { - /* Just register the standard ID registers (read-only, meaning - * that writes will UNDEF). - */ - define_arm_cp_regs(cpu, id_cp_reginfo); } + define_arm_cp_regs(cpu, id_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_AUXCR)) { From 97ce8d61559b99492b4d389eba51a198e55b8455 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Wed, 10 Jul 2013 14:22:21 +1000 Subject: [PATCH 5/8] target-arm/helper.c: Implement MIDR aliases Unimplemented registers in the cp15, CRn=0, opc1=0, CRm=0 space default to aliasing the MIDR register. Set all registers in the space to access MIDR by default. Signed-off-by: Peter Crosthwaite Message-id: 6127846712b7ad2727354a4f5e1d809451f1e859.1373429432.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- target-arm/helper.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 47e6c09387..8d8a8de3bd 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1378,9 +1378,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_DUMMY_C15_REGS)) { define_arm_cp_regs(cpu, dummy_c15_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_MPIDR)) { - define_arm_cp_regs(cpu, mpidr_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_LPAE)) { define_arm_cp_regs(cpu, lpae_cp_reginfo); } @@ -1393,12 +1390,17 @@ void register_cp_regs_for_features(ARMCPU *cpu) /* Note that the MIDR isn't a simple constant register because * of the TI925 behaviour where writes to another register can * cause the MIDR value to change. + * + * Unimplemented registers in the c15 0 0 0 space default to + * MIDR. Define MIDR first as this entire space, then CTR, TCMTR + * and friends override accordingly. */ { .name = "MIDR", - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, + .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, - .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid) }, + .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid), + .type = ARM_CP_OVERRIDE }, { .name = "CTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr }, @@ -1447,6 +1449,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, id_cp_reginfo); } + if (arm_feature(env, ARM_FEATURE_MPIDR)) { + define_arm_cp_regs(cpu, mpidr_cp_reginfo); + } + if (arm_feature(env, ARM_FEATURE_AUXCR)) { ARMCPRegInfo auxcr = { .name = "AUXCR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, From 204a9c43afb92e2ff44fc9d1d377bc98ee97e3d7 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Wed, 10 Jul 2013 14:22:59 +1000 Subject: [PATCH 6/8] target-arm/helper.c: Allow const opaques in arm CP Allow for defining const opaque data in ARM CP register definitions by setting .opaque = foo. If non null opaque is passed into define_one_arm_cp_reg_with_opaque then that opaque will take precedence, otherwise if null opaque is passed, the original opaque data will be used. Signed-off-by: Peter Crosthwaite Message-id: cf0a3ac3438d97464240db9f5f4ef1585cbc1d77.1373429432.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell --- 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 8d8a8de3bd..b7c926db18 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -1608,7 +1608,9 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; *key = ENCODE_CP_REG(r->cp, is64, r->crn, crm, opc1, opc2); - r2->opaque = opaque; + if (opaque) { + r2->opaque = opaque; + } /* Make sure reginfo passed to helpers for wildcarded regs * has the correct crm/opc1/opc2 for this reg, not CP_ANY: */ From 2ebcebe262e88111ff583f97bc5fe0aae64b8940 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jun 2013 16:38:47 +0100 Subject: [PATCH 7/8] target-arm: avoid undefined behaviour when writing TTBCR LPAE CPUs have more potentially valid bits in the TTBCR, and so the simple masking out of invalid bits is no longer sufficient to obtain the base address width field of the register, which is what we use to precalculate c2_mask and c2_base_mask. Explicitly extract the relevant register field rather than simply shifting by the register value. This bug would have had no ill effects in practice, since if the EAE bit (TTBCR bit 31) is set then we don't use the precalculated masks, and if EAE is zero then bits 30..3 are all UNK/SBZP, so well-behaved guests won't set them. However the shift is undefined behaviour, so we should avoid it. Signed-off-by: Peter Maydell Message-id: 1372347527-4428-1-git-send-email-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 b7c926db18..57fa8c85b8 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -891,6 +891,8 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = { static int vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + int maskshift = extract32(value, 0, 3); + if (arm_feature(env, ARM_FEATURE_LPAE)) { value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); } else { @@ -902,8 +904,8 @@ static int vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, * and the c2_mask and c2_base_mask values are meaningless. */ env->cp15.c2_control = value; - env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> value); - env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> value); + env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> maskshift); + env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> maskshift); return 0; } From 82a3a11897308b606120f7235001e87809708f85 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 1 Jul 2013 12:40:19 +0100 Subject: [PATCH 8/8] target-arm: Avoid g_hash_table_get_keys() g_hash_table_get_keys() was only introduced in glib 2.14, and we're still targeting a minimum version of 2.12. Rewrite the offending code (introduced in commit 721fae1) to use g_hash_table_foreach() to build the list of keys. Signed-off-by: Peter Maydell Tested-by: Laurent Desnogues Tested-by: Peter Crosthwaite Message-id: 1372678819-8633-1-git-send-email-peter.maydell@linaro.org --- target-arm/helper.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 57fa8c85b8..aeae024165 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -222,15 +222,23 @@ static gint cpreg_key_compare(gconstpointer a, gconstpointer b) return aidx - bidx; } +static void cpreg_make_keylist(gpointer key, gpointer value, gpointer udata) +{ + GList **plist = udata; + + *plist = g_list_prepend(*plist, key); +} + void init_cpreg_list(ARMCPU *cpu) { /* Initialise the cpreg_tuples[] array based on the cp_regs hash. * Note that we require cpreg_tuples[] to be sorted by key ID. */ - GList *keys; + GList *keys = NULL; int arraylen; - keys = g_hash_table_get_keys(cpu->cp_regs); + g_hash_table_foreach(cpu->cp_regs, cpreg_make_keylist, &keys); + keys = g_list_sort(keys, cpreg_key_compare); cpu->cpreg_array_len = 0;