target-arm: Minimal implementation of performance counters

Upstream 74594c9d813e4d14e9c16cc71824d8905bedc19d

Change-Id: I12de83c519f3170514771c926ea88102009c26bf
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index c84b8b7..3b57045 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -168,9 +168,12 @@
         uint32_t c7_par;  /* Translation result. */
         uint32_t c9_insn; /* Cache lockdown registers.  */
         uint32_t c9_data;
-        uint32_t c9_pmcr_data; /* Performance Monitor Control Register */
-        uint32_t c9_useren; /* user enable register */
-        uint32_t c9_inten; /* interrupt enable set/clear register */
+        uint32_t c9_pmcr; /* performance monitor control register */
+        uint32_t c9_pmcnten; /* perf monitor counter enables */
+        uint32_t c9_pmovsr; /* perf monitor overflow status */
+        uint32_t c9_pmxevtyper; /* perf monitor event type */
+        uint32_t c9_pmuserenr; /* perf monitor user enable */
+        uint32_t c9_pminten; /* perf monitor interrupt enables */
         uint32_t c12_vbar; /* secure/nonsecure vector base address register. */
         uint32_t c12_mvbar; /* monitor vector base address register. */
         uint32_t c13_fcse; /* FCSE PID.  */
@@ -792,7 +795,7 @@
 #define cpu_signal_handler cpu_arm_signal_handler
 #define cpu_list arm_cpu_list
 
-#define CPU_SAVE_VERSION 3
+#define CPU_SAVE_VERSION 4
 
 /* MMU modes definitions */
 #define MMU_MODE0_SUFFIX _kernel
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 5c7158f..5f478d2 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -306,6 +306,10 @@
     }
     env->vfp.xregs[ARM_VFP_FPEXC] = 0;
     env->cp15.c2_base_mask = 0xffffc000u;
+    /* v7 performance monitor control register: same implementor
+     * field as main ID register, and we implement no event counters.
+     */
+    env->cp15.c9_pmcr = (id & 0xff000000);
 #endif
     set_flush_to_zero(1, &env->vfp.standard_fp_status);
     set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
@@ -1660,68 +1664,80 @@
         case 2:
             /* Not implemented.  */
             goto bad_reg;
-        case 12: /* performance monitor control */
-            if (arm_feature(env, ARM_FEATURE_V7)) {
-                switch (op2) {
-                case 0: /* performance monitor control */
-                    env->cp15.c9_pmcr_data = val;
-                    break;
-                case 1: /* count enable set */
-                case 2: /* count enable clear */
-                case 3: /* overflow flag status */
-                case 4: /* software increment */
-                case 5: /* performance counter selection */
-                    /* not implemented */
-                    goto bad_reg;
-                default:
-                    goto bad_reg;
-                }
-            } else {
+        case 12: /* Performance monitor control */
+            /* Performance monitors are implementation defined in v7,
+             * but with an ARM recommended set of registers, which we
+             * follow (although we don't actually implement any counters)
+             */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
+                goto bad_reg;
+            }
+            switch (op2) {
+            case 0: /* performance monitor control register */
+                /* only the DP, X, D and E bits are writable */
+                env->cp15.c9_pmcr &= ~0x39;
+                env->cp15.c9_pmcr |= (val & 0x39);
+                break;
+            case 1: /* Count enable set register */
+                val &= (1 << 31);
+                env->cp15.c9_pmcnten |= val;
+                break;
+            case 2: /* Count enable clear */
+                val &= (1 << 31);
+                env->cp15.c9_pmcnten &= ~val;
+                break;
+            case 3: /* Overflow flag status */
+                env->cp15.c9_pmovsr &= ~val;
+                break;
+            case 4: /* Software increment */
+                /* RAZ/WI since we don't implement the software-count event */
+                break;
+            case 5: /* Event counter selection register */
+                /* Since we don't implement any events, writing to this register
+                 * is actually UNPREDICTABLE. So we choose to RAZ/WI.
+                 */
+                break;
+            default:
                 goto bad_reg;
             }
             break;
-        case 13: /* performance counters */
-            if (arm_feature(env, ARM_FEATURE_V7)) {
-                switch (op2) {
-                case 0: /* cycle count */
-                case 1: /* event selection */
-                case 2: /* performance monitor count */
-                    /* not implemented */
-                    goto bad_reg;
-                default:
-                    goto bad_reg;
-                }
-            } else {
+        case 13: /* Performance counters */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
+                goto bad_reg;
+            }
+            switch (op2) {
+            case 0: /* Cycle count register: not implemented, so RAZ/WI */
+                break;
+            case 1: /* Event type select */
+                env->cp15.c9_pmxevtyper = val & 0xff;
+                break;
+            case 2: /* Event count register */
+                /* Unimplemented (we have no events), RAZ/WI */
+                break;
+            default:
                 goto bad_reg;
             }
             break;
-        case 14: /* performance monitor control */
-            if (arm_feature(env, ARM_FEATURE_V7)) {
-                switch (op2) {
-                case 0: /* user enable */
-                    if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
-                        goto bad_reg;
-                    }
-                    env->cp15.c9_useren = val & 1;
-                    break;
-                case 1: /* interrupt enable set */
-                    if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
-                        goto bad_reg;
-                    }
-                    env->cp15.c9_inten |= val & 0xf;
-                    break;
-                case 2: /* interrupt enable clear */
-                    if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
-                        goto bad_reg;
-                    }
-                    env->cp15.c9_inten &= ~(val & 0xf);
-                    break;
-                default:
-                    goto bad_reg;
-                }
-            } else {
+        case 14: /* Performance monitor control */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
                 goto bad_reg;
             }
+            switch (op2) {
+            case 0: /* user enable */
+                env->cp15.c9_pmuserenr = val & 1;
+                /* changes access rights for cp registers, so flush tbs */
+                tb_flush(env);
+                break;
+            case 1: /* interrupt enable set */
+                /* We have no event counters so only the C bit can be changed */
+                val &= (1 << 31);
+                env->cp15.c9_pminten |= val;
+                break;
+            case 2: /* interrupt enable clear */
+                val &= (1 << 31);
+                env->cp15.c9_pminten &= ~val;
+                break;
+            }
             break;
         default:
             goto bad_reg;
@@ -2063,60 +2079,81 @@
         return 0;
     case 8: /* MMU TLB control.  */
         goto bad_reg;
-    case 9: /* Cache lockdown.  */
-        switch (op1) {
-        case 0:
-	    if (arm_feature(env, ARM_FEATURE_OMAPCP))
-		return 0;
-            switch (crm) {
-            case 0: /* L1 cache */
-            switch (op2) {
-            case 0:
-                return env->cp15.c9_data;
-            case 1:
-                return env->cp15.c9_insn;
-            default:
-                goto bad_reg;
-            }
-                break;
-            case 12:
+    case 9:
+        switch (crm) {
+        case 0: /* Cache lockdown */
+            switch (op1) {
+            case 0: /* L1 cache.  */
+                if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+                    return 0;
+                }
                 switch (op2) {
                 case 0:
-                    return env->cp15.c9_pmcr_data;
+                    return env->cp15.c9_data;
+                case 1:
+                    return env->cp15.c9_insn;
                 default:
                     goto bad_reg;
                 }
-                break;
-            case 14: /* performance monitor control */
-                if (arm_feature(env, ARM_FEATURE_V7)) {
-                    switch (op2) {
-                    case 0: /* user enable */
-                        return env->cp15.c9_useren;
-                    case 1: /* interrupt enable set */
-                    case 2: /* interrupt enable clear */
-                        if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR) {
-                            goto bad_reg;
-                        }
-                        return env->cp15.c9_inten;
-                    default:
-                        goto bad_reg;
-                    }
-                } else {
+            case 1: /* L2 cache */
+                if (crm != 0) {
                     goto bad_reg;
                 }
-                break;
+                /* L2 Lockdown and Auxiliary control.  */
+                return 0;
             default:
                 goto bad_reg;
             }
             break;
-        case 1: /* L2 cache */
-            if (crm != 0)
+        case 12: /* Performance monitor control */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
                 goto bad_reg;
-            /* L2 Lockdown and Auxiliary control.  */
-            return 0;
+            }
+            switch (op2) {
+            case 0: /* performance monitor control register */
+                return env->cp15.c9_pmcr;
+            case 1: /* count enable set */
+            case 2: /* count enable clear */
+                return env->cp15.c9_pmcnten;
+            case 3: /* overflow flag status */
+                return env->cp15.c9_pmovsr;
+            case 4: /* software increment */
+            case 5: /* event counter selection register */
+                return 0; /* Unimplemented, RAZ/WI */
+            default:
+                goto bad_reg;
+            }
+        case 13: /* Performance counters */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
+                goto bad_reg;
+            }
+            switch (op2) {
+            case 1: /* Event type select */
+                return env->cp15.c9_pmxevtyper;
+            case 0: /* Cycle count register */
+            case 2: /* Event count register */
+                /* Unimplemented, so RAZ/WI */
+                return 0;
+            default:
+                goto bad_reg;
+            }
+        case 14: /* Performance monitor control */
+            if (!arm_feature(env, ARM_FEATURE_V7)) {
+                goto bad_reg;
+            }
+            switch (op2) {
+            case 0: /* user enable */
+                return env->cp15.c9_pmuserenr;
+            case 1: /* interrupt enable set */
+            case 2: /* interrupt enable clear */
+                return env->cp15.c9_pminten;
+            default:
+                goto bad_reg;
+            }
         default:
             goto bad_reg;
         }
+        break;
     case 10: /* MMU TLB lockdown.  */
         /* ??? TLB lockdown not implemented.  */
         return 0;
diff --git a/target-arm/machine.c b/target-arm/machine.c
index 61239c3..557e3e0 100644
--- a/target-arm/machine.c
+++ b/target-arm/machine.c
@@ -48,9 +48,12 @@
     qemu_put_be32(f, env->cp15.c7_par);
     qemu_put_be32(f, env->cp15.c9_insn);
     qemu_put_be32(f, env->cp15.c9_data);
-    qemu_put_be32(f, env->cp15.c9_pmcr_data);
-    qemu_put_be32(f, env->cp15.c9_useren);
-    qemu_put_be32(f, env->cp15.c9_inten);
+    qemu_put_be32(f, env->cp15.c9_pmcr);
+    qemu_put_be32(f, env->cp15.c9_pmcnten);
+    qemu_put_be32(f, env->cp15.c9_pmovsr);
+    qemu_put_be32(f, env->cp15.c9_pmxevtyper);
+    qemu_put_be32(f, env->cp15.c9_pmuserenr);
+    qemu_put_be32(f, env->cp15.c9_pminten);
     qemu_put_be32(f, env->cp15.c13_fcse);
     qemu_put_be32(f, env->cp15.c13_context);
     qemu_put_be32(f, env->cp15.c13_tls1);
@@ -111,13 +114,15 @@
     }
 }
 
+#define CPU_SAVE_VERSION_LEGACY  3
+
 int cpu_load(QEMUFile *f, void *opaque, int version_id)
 {
     CPUARMState *env = (CPUARMState *)opaque;
     int i;
     uint32_t val;
 
-    if (version_id != CPU_SAVE_VERSION)
+    if (version_id != CPU_SAVE_VERSION && version_id != CPU_SAVE_VERSION_LEGACY)
         return -EINVAL;
 
     for (i = 0; i < 16; i++) {
@@ -164,9 +169,18 @@
     env->cp15.c7_par = qemu_get_be32(f);
     env->cp15.c9_insn = qemu_get_be32(f);
     env->cp15.c9_data = qemu_get_be32(f);
-    env->cp15.c9_pmcr_data = qemu_get_be32(f);
-    env->cp15.c9_useren = qemu_get_be32(f);
-    env->cp15.c9_inten = qemu_get_be32(f);
+    if (version_id == CPU_SAVE_VERSION_LEGACY) {
+        (void)qemu_get_be32(f);
+        (void)qemu_get_be32(f);
+        (void)qemu_get_be32(f);
+    } else {
+        env->cp15.c9_pmcr = qemu_get_be32(f);
+        env->cp15.c9_pmcnten = qemu_get_be32(f);
+        env->cp15.c9_pmovsr = qemu_get_be32(f);
+        env->cp15.c9_pmxevtyper = qemu_get_be32(f);
+        env->cp15.c9_pmuserenr = qemu_get_be32(f);
+        env->cp15.c9_pminten = qemu_get_be32(f);
+    }
     env->cp15.c13_fcse = qemu_get_be32(f);
     env->cp15.c13_context = qemu_get_be32(f);
     env->cp15.c13_tls1 = qemu_get_be32(f);
diff --git a/target-arm/translate.c b/target-arm/translate.c
index d81b415..38377be 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2468,12 +2468,28 @@
     return 0;
 }
 
-static int cp15_user_ok(uint32_t insn)
+static int cp15_user_ok(CPUARMState *env, uint32_t insn)
 {
     int cpn = (insn >> 16) & 0xf;
     int cpm = insn & 0xf;
     int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
 
+    if (arm_feature(env, ARM_FEATURE_V7) && cpn == 9) {
+        /* Performance monitor registers fall into three categories:
+         *  (a) always UNDEF in usermode
+         *  (b) UNDEF only if PMUSERENR.EN is 0
+         *  (c) always read OK and UNDEF on write (PMUSERENR only)
+         */
+        if ((cpm == 12 && (op < 6)) ||
+            (cpm == 13 && (op < 3))) {
+            return env->cp15.c9_pmuserenr;
+        } else if (cpm == 14 && op == 0 && (insn & ARM_CP_RW_BIT)) {
+            /* PMUSERENR, read only */
+            return 1;
+        }
+        return 0;
+    }
+
     if (cpn == 13 && cpm == 0) {
         /* TLS register.  */
         if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
@@ -2560,7 +2576,7 @@
         /* cdp */
         return 1;
     }
-    if (IS_USER(s) && !cp15_user_ok(insn)) {
+    if (IS_USER(s) && !cp15_user_ok(env, insn)) {
         return 1;
     }