summaryrefslogtreecommitdiff
path: root/target-arm/translate.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2012-06-20 11:57:06 +0000
committerPeter Maydell <peter.maydell@linaro.org>2012-06-20 12:01:02 +0000
commit4b6a83fb0c34a6fcc7bb1058284e3c3674e54421 (patch)
tree96993a63c6c6e60f7936467c1f57658a52c73466 /target-arm/translate.c
parent200bf596b96820186883953de9bda26cac8e6bd7 (diff)
downloadqemu-4b6a83fb0c34a6fcc7bb1058284e3c3674e54421.tar.gz
target-arm: initial coprocessor register framework
Initial infrastructure for data-driven registration of coprocessor register implementations. We still fall back to the old-style switch statements pending complete conversion of all existing registers. Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r--target-arm/translate.c156
1 files changed, 155 insertions, 1 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 437d9dbf0e..d7edda758e 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -6479,13 +6479,16 @@ static int disas_cp14_write(CPUARMState * env, DisasContext *s, uint32_t insn)
static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
{
- int cpnum;
+ int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2;
+ const ARMCPRegInfo *ri;
+ ARMCPU *cpu = arm_env_get_cpu(env);
cpnum = (insn >> 8) & 0xf;
if (arm_feature(env, ARM_FEATURE_XSCALE)
&& ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum)))
return 1;
+ /* First check for coprocessor space used for actual instructions */
switch (cpnum) {
case 0:
case 1:
@@ -6498,6 +6501,157 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
case 10:
case 11:
return disas_vfp_insn (env, s, insn);
+ default:
+ break;
+ }
+
+ /* Otherwise treat as a generic register access */
+ is64 = (insn & (1 << 25)) == 0;
+ if (!is64 && ((insn & (1 << 4)) == 0)) {
+ /* cdp */
+ return 1;
+ }
+
+ crm = insn & 0xf;
+ if (is64) {
+ crn = 0;
+ opc1 = (insn >> 4) & 0xf;
+ opc2 = 0;
+ rt2 = (insn >> 16) & 0xf;
+ } else {
+ crn = (insn >> 16) & 0xf;
+ opc1 = (insn >> 21) & 7;
+ opc2 = (insn >> 5) & 7;
+ rt2 = 0;
+ }
+ isread = (insn >> 20) & 1;
+ rt = (insn >> 12) & 0xf;
+
+ ri = get_arm_cp_reginfo(cpu,
+ ENCODE_CP_REG(cpnum, is64, crn, crm, opc1, opc2));
+ if (ri) {
+ /* Check access permissions */
+ if (!cp_access_ok(env, ri, isread)) {
+ return 1;
+ }
+
+ /* Handle special cases first */
+ switch (ri->type & ~(ARM_CP_FLAG_MASK & ~ARM_CP_SPECIAL)) {
+ case ARM_CP_NOP:
+ return 0;
+ case ARM_CP_WFI:
+ if (isread) {
+ return 1;
+ }
+ gen_set_pc_im(s->pc);
+ s->is_jmp = DISAS_WFI;
+ break;
+ default:
+ break;
+ }
+
+ if (isread) {
+ /* Read */
+ if (is64) {
+ TCGv_i64 tmp64;
+ TCGv_i32 tmp;
+ if (ri->type & ARM_CP_CONST) {
+ tmp64 = tcg_const_i64(ri->resetvalue);
+ } else if (ri->readfn) {
+ TCGv_ptr tmpptr;
+ gen_set_pc_im(s->pc);
+ tmp64 = tcg_temp_new_i64();
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_get_cp_reg64(tmp64, cpu_env, tmpptr);
+ tcg_temp_free_ptr(tmpptr);
+ } else {
+ tmp64 = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp64, cpu_env, ri->fieldoffset);
+ }
+ tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ store_reg(s, rt, tmp);
+ tcg_gen_shri_i64(tmp64, tmp64, 32);
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ store_reg(s, rt2, tmp);
+ } else {
+ TCGv tmp;
+ if (ri->type & ARM_CP_CONST) {
+ tmp = tcg_const_i32(ri->resetvalue);
+ } else if (ri->readfn) {
+ TCGv_ptr tmpptr;
+ gen_set_pc_im(s->pc);
+ tmp = tcg_temp_new_i32();
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_get_cp_reg(tmp, cpu_env, tmpptr);
+ tcg_temp_free_ptr(tmpptr);
+ } else {
+ tmp = load_cpu_offset(ri->fieldoffset);
+ }
+ if (rt == 15) {
+ /* Destination register of r15 for 32 bit loads sets
+ * the condition codes from the high 4 bits of the value
+ */
+ gen_set_nzcv(tmp);
+ tcg_temp_free_i32(tmp);
+ } else {
+ store_reg(s, rt, tmp);
+ }
+ }
+ } else {
+ /* Write */
+ if (ri->type & ARM_CP_CONST) {
+ /* If not forbidden by access permissions, treat as WI */
+ return 0;
+ }
+
+ if (is64) {
+ TCGv tmplo, tmphi;
+ TCGv_i64 tmp64 = tcg_temp_new_i64();
+ tmplo = load_reg(s, rt);
+ tmphi = load_reg(s, rt2);
+ tcg_gen_concat_i32_i64(tmp64, tmplo, tmphi);
+ tcg_temp_free_i32(tmplo);
+ tcg_temp_free_i32(tmphi);
+ if (ri->writefn) {
+ TCGv_ptr tmpptr = tcg_const_ptr(ri);
+ gen_set_pc_im(s->pc);
+ gen_helper_set_cp_reg64(cpu_env, tmpptr, tmp64);
+ tcg_temp_free_ptr(tmpptr);
+ } else {
+ tcg_gen_st_i64(tmp64, cpu_env, ri->fieldoffset);
+ }
+ tcg_temp_free_i64(tmp64);
+ } else {
+ if (ri->writefn) {
+ TCGv tmp;
+ TCGv_ptr tmpptr;
+ gen_set_pc_im(s->pc);
+ tmp = load_reg(s, rt);
+ tmpptr = tcg_const_ptr(ri);
+ gen_helper_set_cp_reg(cpu_env, tmpptr, tmp);
+ tcg_temp_free_ptr(tmpptr);
+ tcg_temp_free_i32(tmp);
+ } else {
+ TCGv tmp = load_reg(s, rt);
+ store_cpu_offset(tmp, ri->fieldoffset);
+ }
+ }
+ /* We default to ending the TB on a coprocessor register write,
+ * but allow this to be suppressed by the register definition
+ * (usually only necessary to work around guest bugs).
+ */
+ if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) {
+ gen_lookup_tb(s);
+ }
+ }
+ return 0;
+ }
+
+ /* Fallback code: handle coprocessor registers not yet converted
+ * to ARMCPRegInfo.
+ */
+ switch (cpnum) {
case 14:
/* Coprocessors 7-15 are architecturally reserved by ARM.
Unfortunately Intel decided to ignore this. */