All of lore.kernel.org
 help / color / mirror / Atom feed
From: Peter Maydell <peter.maydell@linaro.org>
To: Blue Swirl <blauwirbel@gmail.com>
Cc: qemu-devel@nongnu.org, Anthony Liguori <anthony@codemonkey.ws>,
	Paul Brook <paul@codesourcery.com>
Subject: [Qemu-devel] [PATCH 02/33] target-arm: initial coprocessor register framework
Date: Wed, 20 Jun 2012 13:26:50 +0100	[thread overview]
Message-ID: <1340195241-16620-3-git-send-email-peter.maydell@linaro.org> (raw)
In-Reply-To: <1340195241-16620-1-git-send-email-peter.maydell@linaro.org>

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>
---
 target-arm/cpu-qom.h   |    3 +
 target-arm/cpu.c       |   41 ++++++++++
 target-arm/cpu.h       |  201 ++++++++++++++++++++++++++++++++++++++++++++++++
 target-arm/helper.c    |  101 ++++++++++++++++++++++++
 target-arm/helper.h    |    5 +
 target-arm/op_helper.c |   42 ++++++++++-
 target-arm/translate.c |  156 +++++++++++++++++++++++++++++++++++++-
 7 files changed, 546 insertions(+), 3 deletions(-)

diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h
index a61c68d..848ac2f 100644
--- a/target-arm/cpu-qom.h
+++ b/target-arm/cpu-qom.h
@@ -58,6 +58,9 @@ typedef struct ARMCPU {
 
     CPUARMState env;
 
+    /* Coprocessor information */
+    GHashTable *cp_regs;
+
     /* The instance init functions for implementation-specific subclasses
      * set these fields to specify the implementation-dependent values of
      * various constant registers and reset values of non-constant
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 934894b..6456a3d 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -24,6 +24,37 @@
 #include "hw/loader.h"
 #endif
 
+static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
+{
+    /* Reset a single ARMCPRegInfo register */
+    ARMCPRegInfo *ri = value;
+    ARMCPU *cpu = opaque;
+
+    if (ri->type & ARM_CP_SPECIAL) {
+        return;
+    }
+
+    if (ri->resetfn) {
+        ri->resetfn(&cpu->env, ri);
+        return;
+    }
+
+    /* A zero offset is never possible as it would be regs[0]
+     * so we use it to indicate that reset is being handled elsewhere.
+     * This is basically only used for fields in non-core coprocessors
+     * (like the pxa2xx ones).
+     */
+    if (!ri->fieldoffset) {
+        return;
+    }
+
+    if (ri->type & ARM_CP_64BIT) {
+        CPREG_FIELD64(&cpu->env, ri) = ri->resetvalue;
+    } else {
+        CPREG_FIELD32(&cpu->env, ri) = ri->resetvalue;
+    }
+}
+
 /* CPUClass::reset() */
 static void arm_cpu_reset(CPUState *s)
 {
@@ -39,6 +70,7 @@ static void arm_cpu_reset(CPUState *s)
     acc->parent_reset(s);
 
     memset(env, 0, offsetof(CPUARMState, breakpoints));
+    g_hash_table_foreach(cpu->cp_regs, cp_reg_reset, cpu);
     env->cp15.c15_config_base_address = cpu->reset_cbar;
     env->cp15.c0_cpuid = cpu->midr;
     env->vfp.xregs[ARM_VFP_FPSID] = cpu->reset_fpsid;
@@ -130,6 +162,14 @@ static void arm_cpu_initfn(Object *obj)
     ARMCPU *cpu = ARM_CPU(obj);
 
     cpu_exec_init(&cpu->env);
+    cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal,
+                                         g_free, g_free);
+}
+
+static void arm_cpu_finalizefn(Object *obj)
+{
+    ARMCPU *cpu = ARM_CPU(obj);
+    g_hash_table_destroy(cpu->cp_regs);
 }
 
 void arm_cpu_realize(ARMCPU *cpu)
@@ -657,6 +697,7 @@ static const TypeInfo arm_cpu_type_info = {
     .parent = TYPE_CPU,
     .instance_size = sizeof(ARMCPU),
     .instance_init = arm_cpu_initfn,
+    .instance_finalize = arm_cpu_finalizefn,
     .abstract = true,
     .class_size = sizeof(ARMCPUClass),
     .class_init = arm_cpu_class_init,
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index d01285f..6c7d3e7 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -410,6 +410,207 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
                        ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
                        void *opaque);
 
+/* Interface for defining coprocessor registers.
+ * Registers are defined in tables of arm_cp_reginfo structs
+ * which are passed to define_arm_cp_regs().
+ */
+
+/* When looking up a coprocessor register we look for it
+ * via an integer which encodes all of:
+ *  coprocessor number
+ *  Crn, Crm, opc1, opc2 fields
+ *  32 or 64 bit register (ie is it accessed via MRC/MCR
+ *    or via MRRC/MCRR?)
+ * We allow 4 bits for opc1 because MRRC/MCRR have a 4 bit field.
+ * (In this case crn and opc2 should be zero.)
+ */
+#define ENCODE_CP_REG(cp, is64, crn, crm, opc1, opc2)   \
+    (((cp) << 16) | ((is64) << 15) | ((crn) << 11) |    \
+     ((crm) << 7) | ((opc1) << 3) | (opc2))
+
+#define DECODE_CPREG_CRN(enc) (((enc) >> 7) & 0xf)
+
+/* ARMCPRegInfo type field bits. If the SPECIAL bit is set this is a
+ * special-behaviour cp reg and bits [15..8] indicate what behaviour
+ * it has. Otherwise it is a simple cp reg, where CONST indicates that
+ * TCG can assume the value to be constant (ie load at translate time)
+ * and 64BIT indicates a 64 bit wide coprocessor register. SUPPRESS_TB_END
+ * indicates that the TB should not be ended after a write to this register
+ * (the default is that the TB ends after cp writes). OVERRIDE permits
+ * a register definition to override a previous definition for the
+ * same (cp, is64, crn, crm, opc1, opc2) tuple: either the new or the
+ * old must have the OVERRIDE bit set.
+ */
+#define ARM_CP_SPECIAL 1
+#define ARM_CP_CONST 2
+#define ARM_CP_64BIT 4
+#define ARM_CP_SUPPRESS_TB_END 8
+#define ARM_CP_OVERRIDE 16
+#define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8))
+#define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8))
+#define ARM_LAST_SPECIAL ARM_CP_WFI
+/* Used only as a terminator for ARMCPRegInfo lists */
+#define ARM_CP_SENTINEL 0xffff
+/* Mask of only the flag bits in a type field */
+#define ARM_CP_FLAG_MASK 0x1f
+
+/* Return true if cptype is a valid type field. This is used to try to
+ * catch errors where the sentinel has been accidentally left off the end
+ * of a list of registers.
+ */
+static inline bool cptype_valid(int cptype)
+{
+    return ((cptype & ~ARM_CP_FLAG_MASK) == 0)
+        || ((cptype & ARM_CP_SPECIAL) &&
+            (cptype <= ARM_LAST_SPECIAL));
+}
+
+/* Access rights:
+ * We define bits for Read and Write access for what rev C of the v7-AR ARM ARM
+ * defines as PL0 (user), PL1 (fiq/irq/svc/abt/und/sys, ie privileged), and
+ * PL2 (hyp). The other level which has Read and Write bits is Secure PL1
+ * (ie any of the privileged modes in Secure state, or Monitor mode).
+ * If a register is accessible in one privilege level it's always accessible
+ * in higher privilege levels too. Since "Secure PL1" also follows this rule
+ * (ie anything visible in PL2 is visible in S-PL1, some things are only
+ * visible in S-PL1) but "Secure PL1" is a bit of a mouthful, we bend the
+ * terminology a little and call this PL3.
+ *
+ * If access permissions for a register are more complex than can be
+ * described with these bits, then use a laxer set of restrictions, and
+ * do the more restrictive/complex check inside a helper function.
+ */
+#define PL3_R 0x80
+#define PL3_W 0x40
+#define PL2_R (0x20 | PL3_R)
+#define PL2_W (0x10 | PL3_W)
+#define PL1_R (0x08 | PL2_R)
+#define PL1_W (0x04 | PL2_W)
+#define PL0_R (0x02 | PL1_R)
+#define PL0_W (0x01 | PL1_W)
+
+#define PL3_RW (PL3_R | PL3_W)
+#define PL2_RW (PL2_R | PL2_W)
+#define PL1_RW (PL1_R | PL1_W)
+#define PL0_RW (PL0_R | PL0_W)
+
+static inline int arm_current_pl(CPUARMState *env)
+{
+    if ((env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR) {
+        return 0;
+    }
+    /* We don't currently implement the Virtualization or TrustZone
+     * extensions, so PL2 and PL3 don't exist for us.
+     */
+    return 1;
+}
+
+typedef struct ARMCPRegInfo ARMCPRegInfo;
+
+/* Access functions for coprocessor registers. These should return
+ * 0 on success, or one of the EXCP_* constants if access should cause
+ * an exception (in which case *value is not written).
+ */
+typedef int CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque,
+                     uint64_t *value);
+typedef int CPWriteFn(CPUARMState *env, const ARMCPRegInfo *opaque,
+                      uint64_t value);
+/* Hook function for register reset */
+typedef void CPResetFn(CPUARMState *env, const ARMCPRegInfo *opaque);
+
+#define CP_ANY 0xff
+
+/* Definition of an ARM coprocessor register */
+struct ARMCPRegInfo {
+    /* Name of register (useful mainly for debugging, need not be unique) */
+    const char *name;
+    /* Location of register: coprocessor number and (crn,crm,opc1,opc2)
+     * tuple. Any of crm, opc1 and opc2 may be CP_ANY to indicate a
+     * 'wildcard' field -- any value of that field in the MRC/MCR insn
+     * will be decoded to this register. The register read and write
+     * callbacks will be passed an ARMCPRegInfo with the crn/crm/opc1/opc2
+     * used by the program, so it is possible to register a wildcard and
+     * then behave differently on read/write if necessary.
+     * For 64 bit registers, only crm and opc1 are relevant; crn and opc2
+     * must both be zero.
+     */
+    uint8_t cp;
+    uint8_t crn;
+    uint8_t crm;
+    uint8_t opc1;
+    uint8_t opc2;
+    /* Register type: ARM_CP_* bits/values */
+    int type;
+    /* Access rights: PL*_[RW] */
+    int access;
+    /* The opaque pointer passed to define_arm_cp_regs_with_opaque() when
+     * this register was defined: can be used to hand data through to the
+     * register read/write functions, since they are passed the ARMCPRegInfo*.
+     */
+    void *opaque;
+    /* Value of this register, if it is ARM_CP_CONST. Otherwise, if
+     * fieldoffset is non-zero, the reset value of the register.
+     */
+    uint64_t resetvalue;
+    /* Offset of the field in CPUARMState for this register. This is not
+     * needed if either:
+     *  1. type is ARM_CP_CONST or one of the ARM_CP_SPECIALs
+     *  2. both readfn and writefn are specified
+     */
+    ptrdiff_t fieldoffset; /* offsetof(CPUARMState, field) */
+    /* Function for handling reads of this register. If NULL, then reads
+     * will be done by loading from the offset into CPUARMState specified
+     * by fieldoffset.
+     */
+    CPReadFn *readfn;
+    /* Function for handling writes of this register. If NULL, then writes
+     * will be done by writing to the offset into CPUARMState specified
+     * by fieldoffset.
+     */
+    CPWriteFn *writefn;
+    /* Function for resetting the register. If NULL, then reset will be done
+     * by writing resetvalue to the field specified in fieldoffset. If
+     * fieldoffset is 0 then no reset will be done.
+     */
+    CPResetFn *resetfn;
+};
+
+/* Macros which are lvalues for the field in CPUARMState for the
+ * ARMCPRegInfo *ri.
+ */
+#define CPREG_FIELD32(env, ri) \
+    (*(uint32_t *)((char *)(env) + (ri)->fieldoffset))
+#define CPREG_FIELD64(env, ri) \
+    (*(uint64_t *)((char *)(env) + (ri)->fieldoffset))
+
+#define REGINFO_SENTINEL { .type = ARM_CP_SENTINEL }
+
+void define_arm_cp_regs_with_opaque(ARMCPU *cpu,
+                                    const ARMCPRegInfo *regs, void *opaque);
+void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
+                                       const ARMCPRegInfo *regs, void *opaque);
+static inline void define_arm_cp_regs(ARMCPU *cpu, const ARMCPRegInfo *regs)
+{
+    define_arm_cp_regs_with_opaque(cpu, regs, 0);
+}
+static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs)
+{
+    define_one_arm_cp_reg_with_opaque(cpu, regs, 0);
+}
+const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp);
+
+/* CPWriteFn that can be used to implement writes-ignored behaviour */
+int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri,
+                        uint64_t value);
+/* CPReadFn that can be used for read-as-zero behaviour */
+int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value);
+
+static inline bool cp_access_ok(CPUARMState *env,
+                                const ARMCPRegInfo *ri, int isread)
+{
+    return (ri->access >> ((arm_current_pl(env) * 2) + isread)) & 1;
+}
+
 /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
    Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
    conventional cores (ie. Application or Realtime profile).  */
diff --git a/target-arm/helper.c b/target-arm/helper.c
index bbb1d05..7032423 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -137,6 +137,107 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     g_slist_free(list);
 }
 
+void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu,
+                                       const ARMCPRegInfo *r, void *opaque)
+{
+    /* Define implementations of coprocessor registers.
+     * We store these in a hashtable because typically
+     * there are less than 150 registers in a space which
+     * is 16*16*16*8*8 = 262144 in size.
+     * Wildcarding is supported for the crm, opc1 and opc2 fields.
+     * If a register is defined twice then the second definition is
+     * used, so this can be used to define some generic registers and
+     * then override them with implementation specific variations.
+     * At least one of the original and the second definition should
+     * include ARM_CP_OVERRIDE in its type bits -- this is just a guard
+     * against accidental use.
+     */
+    int crm, opc1, opc2;
+    int crmmin = (r->crm == CP_ANY) ? 0 : r->crm;
+    int crmmax = (r->crm == CP_ANY) ? 15 : r->crm;
+    int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1;
+    int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1;
+    int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2;
+    int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2;
+    /* 64 bit registers have only CRm and Opc1 fields */
+    assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn)));
+    /* Check that the register definition has enough info to handle
+     * reads and writes if they are permitted.
+     */
+    if (!(r->type & (ARM_CP_SPECIAL|ARM_CP_CONST))) {
+        if (r->access & PL3_R) {
+            assert(r->fieldoffset || r->readfn);
+        }
+        if (r->access & PL3_W) {
+            assert(r->fieldoffset || r->writefn);
+        }
+    }
+    /* Bad type field probably means missing sentinel at end of reg list */
+    assert(cptype_valid(r->type));
+    for (crm = crmmin; crm <= crmmax; crm++) {
+        for (opc1 = opc1min; opc1 <= opc1max; opc1++) {
+            for (opc2 = opc2min; opc2 <= opc2max; opc2++) {
+                uint32_t *key = g_new(uint32_t, 1);
+                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;
+                /* Make sure reginfo passed to helpers for wildcarded regs
+                 * has the correct crm/opc1/opc2 for this reg, not CP_ANY:
+                 */
+                r2->crm = crm;
+                r2->opc1 = opc1;
+                r2->opc2 = opc2;
+                /* Overriding of an existing definition must be explicitly
+                 * requested.
+                 */
+                if (!(r->type & ARM_CP_OVERRIDE)) {
+                    ARMCPRegInfo *oldreg;
+                    oldreg = g_hash_table_lookup(cpu->cp_regs, key);
+                    if (oldreg && !(oldreg->type & ARM_CP_OVERRIDE)) {
+                        fprintf(stderr, "Register redefined: cp=%d %d bit "
+                                "crn=%d crm=%d opc1=%d opc2=%d, "
+                                "was %s, now %s\n", r2->cp, 32 + 32 * is64,
+                                r2->crn, r2->crm, r2->opc1, r2->opc2,
+                                oldreg->name, r2->name);
+                        assert(0);
+                    }
+                }
+                g_hash_table_insert(cpu->cp_regs, key, r2);
+            }
+        }
+    }
+}
+
+void define_arm_cp_regs_with_opaque(ARMCPU *cpu,
+                                    const ARMCPRegInfo *regs, void *opaque)
+{
+    /* Define a whole list of registers */
+    const ARMCPRegInfo *r;
+    for (r = regs; r->type != ARM_CP_SENTINEL; r++) {
+        define_one_arm_cp_reg_with_opaque(cpu, r, opaque);
+    }
+}
+
+const ARMCPRegInfo *get_arm_cp_reginfo(ARMCPU *cpu, uint32_t encoded_cp)
+{
+    return g_hash_table_lookup(cpu->cp_regs, &encoded_cp);
+}
+
+int arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri,
+                        uint64_t value)
+{
+    /* Helper coprocessor write function for write-ignore registers */
+    return 0;
+}
+
+int arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value)
+{
+    /* Helper coprocessor write function for read-as-zero registers */
+    *value = 0;
+    return 0;
+}
+
 static int bad_mode_switch(CPUARMState *env, int mode)
 {
     /* Return true if it is not valid for us to switch to
diff --git a/target-arm/helper.h b/target-arm/helper.h
index 16dd5fc..b6cefed 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -65,6 +65,11 @@ DEF_HELPER_2(get_cp15, i32, env, i32)
 DEF_HELPER_3(set_cp, void, env, i32, i32)
 DEF_HELPER_2(get_cp, i32, env, i32)
 
+DEF_HELPER_3(set_cp_reg, void, env, ptr, i32)
+DEF_HELPER_2(get_cp_reg, i32, env, ptr)
+DEF_HELPER_3(set_cp_reg64, void, env, ptr, i64)
+DEF_HELPER_2(get_cp_reg64, i64, env, ptr)
+
 DEF_HELPER_2(get_r13_banked, i32, env, i32)
 DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
 
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
index b53369d..490111c 100644
--- a/target-arm/op_helper.c
+++ b/target-arm/op_helper.c
@@ -23,13 +23,11 @@
 #define SIGNBIT (uint32_t)0x80000000
 #define SIGNBIT64 ((uint64_t)1 << 63)
 
-#if !defined(CONFIG_USER_ONLY)
 static void raise_exception(int tt)
 {
     env->exception_index = tt;
     cpu_loop_exit(env);
 }
-#endif
 
 uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
                           uint32_t rn, uint32_t maxindex)
@@ -287,6 +285,46 @@ void HELPER(set_user_reg)(uint32_t regno, uint32_t val)
     }
 }
 
+void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
+{
+    const ARMCPRegInfo *ri = rip;
+    int excp = ri->writefn(env, ri, value);
+    if (excp) {
+        raise_exception(excp);
+    }
+}
+
+uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
+{
+    const ARMCPRegInfo *ri = rip;
+    uint64_t value;
+    int excp = ri->readfn(env, ri, &value);
+    if (excp) {
+        raise_exception(excp);
+    }
+    return value;
+}
+
+void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
+{
+    const ARMCPRegInfo *ri = rip;
+    int excp = ri->writefn(env, ri, value);
+    if (excp) {
+        raise_exception(excp);
+    }
+}
+
+uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
+{
+    const ARMCPRegInfo *ri = rip;
+    uint64_t value;
+    int excp = ri->readfn(env, ri, &value);
+    if (excp) {
+        raise_exception(excp);
+    }
+    return value;
+}
+
 /* ??? Flag setting arithmetic is awkward because we need to do comparisons.
    The only way to do that in TCG is a conditional branch, which clobbers
    all our temporaries.  For now implement these as helper functions.  */
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 437d9db..d7edda7 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.  */
-- 
1.7.1

  parent reply	other threads:[~2012-06-20 12:57 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-20 12:26 [Qemu-devel] [PULL 00/33] target-arm queue Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 01/33] target-arm: Fix 11MPCore cache type register value Peter Maydell
2012-06-20 12:26 ` Peter Maydell [this message]
2012-06-20 12:26 ` [Qemu-devel] [PATCH 03/33] hw/pxa2xx: Convert cp14 perf registers to new scheme Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 04/33] hw/pxa2xx.c: Convert CLKCFG and PWRMODE cp14 regs Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 05/33] hw/pxa2xx_pic: Convert coprocessor registers to new scheme Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 06/33] target-arm: Remove old cpu_arm_set_cp_io infrastructure Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 07/33] target-arm: Add register_cp_regs_for_features() Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 08/33] target-arm: Convert debug registers to cp_reginfo Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 09/33] target-arm: Convert TEECR, TEEHBR to new scheme Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 10/33] target-arm: Convert WFI/barriers special cases to cp_reginfo Peter Maydell
2012-06-20 12:26 ` [Qemu-devel] [PATCH 11/33] target-arm: Convert TLS registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 12/33] target-arm: Convert performance monitor registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 13/33] target-arm: Convert generic timer cp15 regs Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 14/33] target-arm: Convert cp15 c3 register Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 15/33] target-arm: Convert MMU fault status cp15 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 16/33] target-arm: Convert cp15 crn=2 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 17/33] target-arm: Convert cp15 crn=13 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 18/33] target-arm: Convert cp15 crn=10 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 19/33] target-arm: Convert cp15 crn=15 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 20/33] target-arm: Convert cp15 MMU TLB control Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 21/33] target-arm: Convert cp15 VA-PA translation registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 22/33] target-arm: convert cp15 crn=7 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 23/33] target-arm: Convert cp15 crn=6 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 24/33] target-arm: Convert cp15 crn=9 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 25/33] target-arm: Convert cp15 crn=1 registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 26/33] target-arm: Convert cp15 crn=0 crm={1, 2} feature registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 27/33] target-arm: Convert cp15 cache ID registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 28/33] target-arm: Convert MPIDR Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 29/33] target-arm: Convert final ID registers Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 30/33] target-arm: Remove c0_cachetype CPUARMState field Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 31/33] target-arm: Move block cache ops to new cp15 framework Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 32/33] target-arm: Remove remaining old cp15 infrastructure Peter Maydell
2012-06-20 12:27 ` [Qemu-devel] [PATCH 33/33] target-arm: Remove ARM_CPUID_* macros Peter Maydell
2012-06-24 12:27 ` [Qemu-devel] [PULL 00/33] target-arm queue Blue Swirl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1340195241-16620-3-git-send-email-peter.maydell@linaro.org \
    --to=peter.maydell@linaro.org \
    --cc=anthony@codemonkey.ws \
    --cc=blauwirbel@gmail.com \
    --cc=paul@codesourcery.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.