]> Git Repo - qemu.git/blobdiff - target-arm/helper.c
target-arm: Add 64 bit PAR, TTBR0, TTBR1 for LPAE
[qemu.git] / target-arm / helper.c
index 8def356dfc9e406820a3f5522093b64c587aa5a7..6bb6767bc26bb2836d46f406bc378e1e797646aa 100644 (file)
@@ -4,6 +4,13 @@
 #include "host-utils.h"
 #include "sysemu.h"
 
+#ifndef CONFIG_USER_ONLY
+static inline int get_phys_addr(CPUARMState *env, uint32_t address,
+                                int access_type, int is_user,
+                                uint32_t *phys_ptr, int *prot,
+                                target_ulong *page_size);
+#endif
+
 static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
 {
     int nregs;
@@ -154,6 +161,10 @@ static const ARMCPRegInfo cp_reginfo[] = {
       .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, },
     { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY,
       .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, },
+    /* Cache maintenance ops; some of this space may be overridden later. */
+    { .name = "CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY,
+      .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W,
+      .type = ARM_CP_NOP | ARM_CP_OVERRIDE },
     REGINFO_SENTINEL
 };
 
@@ -172,9 +183,32 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = {
      */
     { .name = "WFI_v6", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
       .access = PL1_W, .type = ARM_CP_WFI },
+    /* L1 cache lockdown. Not architectural in v6 and earlier but in practice
+     * implemented in 926, 946, 1026, 1136, 1176 and 11MPCore. StrongARM and
+     * OMAPCP will override this space.
+     */
+    { .name = "DLOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_data),
+      .resetvalue = 0 },
+    { .name = "ILOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_insn),
+      .resetvalue = 0 },
+    /* v6 doesn't have the cache ID registers but Linux reads them anyway */
+    { .name = "DUMMY", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = CP_ANY,
+      .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
     REGINFO_SENTINEL
 };
 
+static int cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+    if (env->cp15.c1_coproc != value) {
+        env->cp15.c1_coproc = value;
+        /* ??? Is this safe when called from within a TB?  */
+        tb_flush(env);
+    }
+    return 0;
+}
+
 static const ARMCPRegInfo v6_cp_reginfo[] = {
     /* prefetch by MVA in v6, NOP in v7 */
     { .name = "MVA_prefetch",
@@ -182,10 +216,21 @@ static const ARMCPRegInfo v6_cp_reginfo[] = {
       .access = PL1_W, .type = ARM_CP_NOP },
     { .name = "ISB", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 4,
       .access = PL0_W, .type = ARM_CP_NOP },
-    { .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4,
+    { .name = "DSB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4,
       .access = PL0_W, .type = ARM_CP_NOP },
-    { .name = "ISB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5,
+    { .name = "DMB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 5,
       .access = PL0_W, .type = ARM_CP_NOP },
+    { .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 2,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_insn),
+      .resetvalue = 0, },
+    /* Watchpoint Fault Address Register : should actually only be present
+     * for 1136, 1176, 11MPCore.
+     */
+    { .name = "WFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0, },
+    { .name = "CPACR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_coproc),
+      .resetvalue = 0, .writefn = cpacr_write },
     REGINFO_SENTINEL
 };
 
@@ -280,13 +325,28 @@ static int pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri,
     return 0;
 }
 
+static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                       uint64_t *value)
+{
+    ARMCPU *cpu = arm_env_get_cpu(env);
+    *value = cpu->ccsidr[env->cp15.c0_cssel];
+    return 0;
+}
+
+static int csselr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                        uint64_t value)
+{
+    env->cp15.c0_cssel = value & 0xf;
+    return 0;
+}
+
 static const ARMCPRegInfo v7_cp_reginfo[] = {
     /* DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped
      * debug components
      */
     { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
       .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
-    { .name = "DBGDRAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
+    { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
       .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
     /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */
     { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4,
@@ -347,6 +407,19 @@ static const ARMCPRegInfo v7_cp_reginfo[] = {
       .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
       .resetvalue = 0,
       .writefn = pmintenclr_write },
+    { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr),
+      .resetvalue = 0, },
+    { .name = "CCSIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0,
+      .access = PL1_R, .readfn = ccsidr_read },
+    { .name = "CSSELR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c0_cssel),
+      .writefn = csselr_write, .resetvalue = 0 },
+    /* Auxiliary ID register: this actually has an IMPDEF value but for now
+     * just RAZ for all cores:
+     */
+    { .name = "AIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 7,
+      .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
     REGINFO_SENTINEL
 };
 
@@ -416,6 +489,64 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
     REGINFO_SENTINEL
 };
 
+static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+    if (arm_feature(env, ARM_FEATURE_LPAE)) {
+        env->cp15.c7_par = value;
+    } else if (arm_feature(env, ARM_FEATURE_V7)) {
+        env->cp15.c7_par = value & 0xfffff6ff;
+    } else {
+        env->cp15.c7_par = value & 0xfffff1ff;
+    }
+    return 0;
+}
+
+#ifndef CONFIG_USER_ONLY
+/* get_phys_addr() isn't present for user-mode-only targets */
+static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+    uint32_t phys_addr;
+    target_ulong page_size;
+    int prot;
+    int ret, is_user = ri->opc2 & 2;
+    int access_type = ri->opc2 & 1;
+
+    if (ri->opc2 & 4) {
+        /* Other states are only available with TrustZone */
+        return EXCP_UDEF;
+    }
+    ret = get_phys_addr(env, value, access_type, is_user,
+                        &phys_addr, &prot, &page_size);
+    if (ret == 0) {
+        /* We do not set any attribute bits in the PAR */
+        if (page_size == (1 << 24)
+            && arm_feature(env, ARM_FEATURE_V7)) {
+            env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1;
+        } else {
+            env->cp15.c7_par = phys_addr & 0xfffff000;
+        }
+    } else {
+        env->cp15.c7_par = ((ret & (10 << 1)) >> 5) |
+            ((ret & (12 << 1)) >> 6) |
+            ((ret & 0xf) << 1) | 1;
+    }
+    env->cp15.c7_par_hi = 0;
+    return 0;
+}
+#endif
+
+static const ARMCPRegInfo vapa_cp_reginfo[] = {
+    { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .resetvalue = 0,
+      .fieldoffset = offsetof(CPUARMState, cp15.c7_par),
+      .writefn = par_write },
+#ifndef CONFIG_USER_ONLY
+    { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY,
+      .access = PL1_W, .writefn = ats_write },
+#endif
+    REGINFO_SENTINEL
+};
+
 /* Return basic MPU access permission bits.  */
 static uint32_t simple_mpu_ap_bits(uint32_t val)
 {
@@ -474,6 +605,26 @@ static int pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri,
     return 0;
 }
 
+static int arm946_prbs_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                            uint64_t *value)
+{
+    if (ri->crm > 8) {
+        return EXCP_UDEF;
+    }
+    *value = env->cp15.c6_region[ri->crm];
+    return 0;
+}
+
+static int arm946_prbs_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                             uint64_t value)
+{
+    if (ri->crm > 8) {
+        return EXCP_UDEF;
+    }
+    env->cp15.c6_region[ri->crm] = value;
+    return 0;
+}
+
 static const ARMCPRegInfo pmsav5_cp_reginfo[] = {
     { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
       .access = PL1_RW,
@@ -495,6 +646,10 @@ static const ARMCPRegInfo pmsav5_cp_reginfo[] = {
     { .name = "ICACHE_CFG", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
       .access = PL1_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c2_insn), .resetvalue = 0, },
+    /* Protection region base and size registers */
+    { .name = "946_PRBS", .cp = 15, .crn = 6, .crm = CP_ANY, .opc1 = 0,
+      .opc2 = CP_ANY, .access = PL1_RW,
+      .readfn = arm946_prbs_read, .writefn = arm946_prbs_write, },
     REGINFO_SENTINEL
 };
 
@@ -527,11 +682,14 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
       .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
     { .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
       .access = PL1_RW,
-      .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
+      .fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, },
     { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
       .access = PL1_RW, .writefn = vmsa_ttbcr_write,
       .resetfn = vmsa_ttbcr_reset,
       .fieldoffset = offsetof(CPUARMState, cp15.c2_control) },
+    { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data),
+      .resetvalue = 0, },
     REGINFO_SENTINEL
 };
 
@@ -560,6 +718,17 @@ static int omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri,
     return 0;
 }
 
+static int omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                                 uint64_t value)
+{
+    /* On OMAP there are registers indicating the max/min index of dcache lines
+     * containing a dirty line; cache flush operations have to reset these.
+     */
+    env->cp15.c15_i_max = 0x000;
+    env->cp15.c15_i_min = 0xff0;
+    return 0;
+}
+
 static const ARMCPRegInfo omap_cp_reginfo[] = {
     { .name = "DFSR", .cp = 15, .crn = 5, .crm = CP_ANY,
       .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_OVERRIDE,
@@ -588,6 +757,12 @@ static const ARMCPRegInfo omap_cp_reginfo[] = {
      * base address at $rn & ~0xfff and map size of 0x200 << ($rn & 0xfff),
      * when MMU is off.
      */
+    { .name = "OMAP_CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY,
+      .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_OVERRIDE,
+      .writefn = omap_cachemaint_write },
+    { .name = "C9", .cp = 15, .crn = 9,
+      .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW,
+      .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 },
     REGINFO_SENTINEL
 };
 
@@ -608,6 +783,10 @@ static const ARMCPRegInfo xscale_cp_reginfo[] = {
       .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c15_cpar), .resetvalue = 0,
       .writefn = xscale_cpar_write, },
+    { .name = "XSCALE_AUXCR",
+      .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW,
+      .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr),
+      .resetvalue = 0, },
     REGINFO_SENTINEL
 };
 
@@ -623,6 +802,177 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = {
     REGINFO_SENTINEL
 };
 
+static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = {
+    /* Cache status: RAZ because we have no cache so it's always clean */
+    { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6,
+      .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+    REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = {
+    /* We never have a a block transfer operation in progress */
+    { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4,
+      .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+    /* The cache ops themselves: these all NOP for QEMU */
+    { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0,
+      .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    { .name = "IDCR", .cp = 15, .crm = 6, .opc1 = 0,
+      .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    { .name = "CDCR", .cp = 15, .crm = 12, .opc1 = 0,
+      .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    { .name = "PIR", .cp = 15, .crm = 12, .opc1 = 1,
+      .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    { .name = "PDR", .cp = 15, .crm = 12, .opc1 = 2,
+      .access = PL0_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    { .name = "CIDCR", .cp = 15, .crm = 14, .opc1 = 0,
+      .access = PL1_W, .type = ARM_CP_NOP|ARM_CP_64BIT },
+    REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = {
+    /* The cache test-and-clean instructions always return (1 << 30)
+     * to indicate that there are no dirty cache lines.
+     */
+    { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3,
+      .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) },
+    { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3,
+      .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = (1 << 30) },
+    REGINFO_SENTINEL
+};
+
+static const ARMCPRegInfo strongarm_cp_reginfo[] = {
+    /* Ignore ReadBuffer accesses */
+    { .name = "C9_READBUFFER", .cp = 15, .crn = 9,
+      .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
+      .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
+      .resetvalue = 0 },
+    REGINFO_SENTINEL
+};
+
+static int mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                      uint64_t *value)
+{
+    uint32_t mpidr = env->cpu_index;
+    /* We don't support setting cluster ID ([8..11])
+     * so these bits always RAZ.
+     */
+    if (arm_feature(env, ARM_FEATURE_V7MP)) {
+        mpidr |= (1 << 31);
+        /* Cores which are uniprocessor (non-coherent)
+         * but still implement the MP extensions set
+         * bit 30. (For instance, A9UP.) However we do
+         * not currently model any of those cores.
+         */
+    }
+    *value = mpidr;
+    return 0;
+}
+
+static const ARMCPRegInfo mpidr_cp_reginfo[] = {
+    { .name = "MPIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5,
+      .access = PL1_R, .readfn = mpidr_read },
+    REGINFO_SENTINEL
+};
+
+static int par64_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value)
+{
+    *value = ((uint64_t)env->cp15.c7_par_hi << 32) | env->cp15.c7_par;
+    return 0;
+}
+
+static int par64_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+    env->cp15.c7_par_hi = value >> 32;
+    env->cp15.c7_par = value;
+    return 0;
+}
+
+static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+    env->cp15.c7_par_hi = 0;
+    env->cp15.c7_par = 0;
+}
+
+static int ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                        uint64_t *value)
+{
+    *value = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
+    return 0;
+}
+
+static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                         uint64_t value)
+{
+    env->cp15.c2_base0_hi = value >> 32;
+    env->cp15.c2_base0 = value;
+    /* Writes to the 64 bit format TTBRs may change the ASID */
+    tlb_flush(env, 1);
+    return 0;
+}
+
+static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+    env->cp15.c2_base0_hi = 0;
+    env->cp15.c2_base0 = 0;
+}
+
+static int ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri,
+                        uint64_t *value)
+{
+    *value = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
+    return 0;
+}
+
+static int ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                         uint64_t value)
+{
+    env->cp15.c2_base1_hi = value >> 32;
+    env->cp15.c2_base1 = value;
+    return 0;
+}
+
+static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri)
+{
+    env->cp15.c2_base1_hi = 0;
+    env->cp15.c2_base1 = 0;
+}
+
+static const ARMCPRegInfo lpae_cp_reginfo[] = {
+    /* NOP AMAIR0/1: the override is because these clash with tha rather
+     * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
+     */
+    { .name = "AMAIR0", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
+      .resetvalue = 0 },
+    { .name = "AMAIR1", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_OVERRIDE,
+      .resetvalue = 0 },
+    /* 64 bit access versions of the (dummy) debug registers */
+    { .name = "DBGDRAR", .cp = 14, .crm = 1, .opc1 = 0,
+      .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 },
+    { .name = "DBGDSAR", .cp = 14, .crm = 2, .opc1 = 0,
+      .access = PL0_R, .type = ARM_CP_CONST|ARM_CP_64BIT, .resetvalue = 0 },
+    { .name = "PAR", .cp = 15, .crm = 7, .opc1 = 0,
+      .access = PL1_RW, .type = ARM_CP_64BIT,
+      .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
+    { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
+      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read,
+      .writefn = ttbr064_write, .resetfn = ttbr064_reset },
+    { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
+      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read,
+      .writefn = ttbr164_write, .resetfn = ttbr164_reset },
+    REGINFO_SENTINEL
+};
+
+static int sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+    env->cp15.c1_sys = value;
+    /* ??? Lots of these bits are not implemented.  */
+    /* This may enable/disable the MMU, so do a TLB flush.  */
+    tlb_flush(env, 1);
+    return 0;
+}
+
 void register_cp_regs_for_features(ARMCPU *cpu)
 {
     /* Register all the coprocessor registers based on feature bits */
@@ -634,6 +984,60 @@ void register_cp_regs_for_features(ARMCPU *cpu)
 
     define_arm_cp_regs(cpu, cp_reginfo);
     if (arm_feature(env, ARM_FEATURE_V6)) {
+        /* The ID registers all have impdef reset values */
+        ARMCPRegInfo v6_idregs[] = {
+            { .name = "ID_PFR0", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_pfr0 },
+            { .name = "ID_PFR1", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_pfr1 },
+            { .name = "ID_DFR0", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_dfr0 },
+            { .name = "ID_AFR0", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_afr0 },
+            { .name = "ID_MMFR0", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_mmfr0 },
+            { .name = "ID_MMFR1", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_mmfr1 },
+            { .name = "ID_MMFR2", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_mmfr2 },
+            { .name = "ID_MMFR3", .cp = 15, .crn = 0, .crm = 1,
+              .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_mmfr3 },
+            { .name = "ID_ISAR0", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar0 },
+            { .name = "ID_ISAR1", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar1 },
+            { .name = "ID_ISAR2", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar2 },
+            { .name = "ID_ISAR3", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar3 },
+            { .name = "ID_ISAR4", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar4 },
+            { .name = "ID_ISAR5", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = cpu->id_isar5 },
+            /* 6..7 are as yet unallocated and must RAZ */
+            { .name = "ID_ISAR6", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = 0 },
+            { .name = "ID_ISAR7", .cp = 15, .crn = 0, .crm = 2,
+              .opc1 = 0, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST,
+              .resetvalue = 0 },
+            REGINFO_SENTINEL
+        };
+        define_arm_cp_regs(cpu, v6_idregs);
         define_arm_cp_regs(cpu, v6_cp_reginfo);
     } else {
         define_arm_cp_regs(cpu, not_v6_cp_reginfo);
@@ -651,7 +1055,12 @@ void register_cp_regs_for_features(ARMCPU *cpu)
             .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),
             .readfn = pmreg_read, .writefn = pmcr_write
         };
+        ARMCPRegInfo clidr = {
+            .name = "CLIDR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1,
+            .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->clidr
+        };
         define_one_arm_cp_reg(cpu, &pmcr);
+        define_one_arm_cp_reg(cpu, &clidr);
         define_arm_cp_regs(cpu, v7_cp_reginfo);
     } else {
         define_arm_cp_regs(cpu, not_v7_cp_reginfo);
@@ -673,15 +1082,129 @@ void register_cp_regs_for_features(ARMCPU *cpu)
     if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) {
         define_arm_cp_regs(cpu, generic_timer_cp_reginfo);
     }
+    if (arm_feature(env, ARM_FEATURE_VAPA)) {
+        define_arm_cp_regs(cpu, vapa_cp_reginfo);
+    }
+    if (arm_feature(env, ARM_FEATURE_CACHE_TEST_CLEAN)) {
+        define_arm_cp_regs(cpu, cache_test_clean_cp_reginfo);
+    }
+    if (arm_feature(env, ARM_FEATURE_CACHE_DIRTY_REG)) {
+        define_arm_cp_regs(cpu, cache_dirty_status_cp_reginfo);
+    }
+    if (arm_feature(env, ARM_FEATURE_CACHE_BLOCK_OPS)) {
+        define_arm_cp_regs(cpu, cache_block_ops_cp_reginfo);
+    }
     if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
         define_arm_cp_regs(cpu, omap_cp_reginfo);
     }
+    if (arm_feature(env, ARM_FEATURE_STRONGARM)) {
+        define_arm_cp_regs(cpu, strongarm_cp_reginfo);
+    }
     if (arm_feature(env, ARM_FEATURE_XSCALE)) {
         define_arm_cp_regs(cpu, xscale_cp_reginfo);
     }
     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);
+    }
+    /* Slightly awkwardly, the OMAP and StrongARM cores need all of
+     * cp15 crn=0 to be writes-ignored, whereas for other cores they should
+     * be read-only (ie write causes UNDEF exception).
+     */
+    {
+        ARMCPRegInfo id_cp_reginfo[] = {
+            /* 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.
+             */
+            { .name = "MIDR",
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0,
+              .access = PL1_R, .resetvalue = cpu->midr,
+              .writefn = arm_cp_write_ignore,
+              .fieldoffset = offsetof(CPUARMState, cp15.c0_cpuid) },
+            { .name = "CTR",
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 1,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->ctr },
+            { .name = "TCMTR",
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            { .name = "TLBTR",
+              .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            /* crn = 0 op1 = 0 crm = 3..7 : currently unassigned; we RAZ. */
+            { .name = "DUMMY",
+              .cp = 15, .crn = 0, .crm = 3, .opc1 = 0, .opc2 = CP_ANY,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            { .name = "DUMMY",
+              .cp = 15, .crn = 0, .crm = 4, .opc1 = 0, .opc2 = CP_ANY,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            { .name = "DUMMY",
+              .cp = 15, .crn = 0, .crm = 5, .opc1 = 0, .opc2 = CP_ANY,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            { .name = "DUMMY",
+              .cp = 15, .crn = 0, .crm = 6, .opc1 = 0, .opc2 = CP_ANY,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            { .name = "DUMMY",
+              .cp = 15, .crn = 0, .crm = 7, .opc1 = 0, .opc2 = CP_ANY,
+              .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 },
+            REGINFO_SENTINEL
+        };
+        ARMCPRegInfo crn0_wi_reginfo = {
+            .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY,
+            .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W,
+            .type = ARM_CP_NOP | ARM_CP_OVERRIDE
+        };
+        if (arm_feature(env, ARM_FEATURE_OMAPCP) ||
+            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.
+             */
+            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);
+        }
+    }
+
+    if (arm_feature(env, ARM_FEATURE_AUXCR)) {
+        ARMCPRegInfo auxcr = {
+            .name = "AUXCR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1,
+            .access = PL1_RW, .type = ARM_CP_CONST,
+            .resetvalue = cpu->reset_auxcr
+        };
+        define_one_arm_cp_reg(cpu, &auxcr);
+    }
+
+    /* Generic registers whose values depend on the implementation */
+    {
+        ARMCPRegInfo sctlr = {
+            .name = "SCTLR", .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0,
+            .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_sys),
+            .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr
+        };
+        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+            /* Normally we would always end the TB on an SCTLR write, but Linux
+             * arch/arm/mach-pxa/sleep.S expects two instructions following
+             * an MMU enable to execute from cache.  Imitate this behaviour.
+             */
+            sctlr.type |= ARM_CP_SUPPRESS_TB_END;
+        }
+        define_one_arm_cp_reg(cpu, &sctlr);
+    }
 }
 
 ARMCPU *cpu_arm_init(const char *cpu_model)
@@ -1014,16 +1537,6 @@ int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
     return 1;
 }
 
-void HELPER(set_cp15)(CPUARMState *env, uint32_t insn, uint32_t val)
-{
-    cpu_abort(env, "cp15 insn %08x\n", insn);
-}
-
-uint32_t HELPER(get_cp15)(CPUARMState *env, uint32_t insn)
-{
-    cpu_abort(env, "cp15 insn %08x\n", insn);
-}
-
 /* These should probably raise undefined insn exceptions.  */
 void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val)
 {
@@ -1518,9 +2031,10 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
     uint32_t table;
     uint32_t desc;
     uint32_t xn;
+    uint32_t pxn = 0;
     int type;
     int ap;
-    int domain;
+    int domain = 0;
     int domain_prot;
     uint32_t phys_addr;
 
@@ -1529,27 +2043,27 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
     table = get_level1_table_address(env, address);
     desc = ldl_phys(table);
     type = (desc & 3);
-    if (type == 0) {
-        /* Section translation fault.  */
+    if (type == 0 || (type == 3 && !arm_feature(env, ARM_FEATURE_PXN))) {
+        /* Section translation fault, or attempt to use the encoding
+         * which is Reserved on implementations without PXN.
+         */
         code = 5;
-        domain = 0;
         goto do_fault;
-    } else if (type == 2 && (desc & (1 << 18))) {
-        /* Supersection.  */
-        domain = 0;
-    } else {
-        /* Section or page.  */
+    }
+    if ((type == 1) || !(desc & (1 << 18))) {
+        /* Page or Section.  */
         domain = (desc >> 5) & 0x0f;
     }
     domain_prot = (env->cp15.c3 >> (domain * 2)) & 3;
     if (domain_prot == 0 || domain_prot == 2) {
-        if (type == 2)
+        if (type != 1) {
             code = 9; /* Section domain fault.  */
-        else
+        } else {
             code = 11; /* Page domain fault.  */
+        }
         goto do_fault;
     }
-    if (type == 2) {
+    if (type != 1) {
         if (desc & (1 << 18)) {
             /* Supersection.  */
             phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
@@ -1561,8 +2075,12 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
         }
         ap = ((desc >> 10) & 3) | ((desc >> 13) & 4);
         xn = desc & (1 << 4);
+        pxn = desc & 1;
         code = 13;
     } else {
+        if (arm_feature(env, ARM_FEATURE_PXN)) {
+            pxn = (desc >> 2) & 1;
+        }
         /* Lookup l2 entry.  */
         table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
         desc = ldl_phys(table);
@@ -1590,6 +2108,9 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
     if (domain_prot == 3) {
         *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
     } else {
+        if (pxn && !is_user) {
+            xn = 1;
+        }
         if (xn && access_type == 2)
             goto do_fault;
 
@@ -1750,402 +2271,6 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUARMState *env, target_ulong addr)
     return phys_addr;
 }
 
-void HELPER(set_cp15)(CPUARMState *env, uint32_t insn, uint32_t val)
-{
-    int op1;
-    int op2;
-    int crm;
-
-    op1 = (insn >> 21) & 7;
-    op2 = (insn >> 5) & 7;
-    crm = insn & 0xf;
-    switch ((insn >> 16) & 0xf) {
-    case 0:
-        /* ID codes.  */
-        if (arm_feature(env, ARM_FEATURE_XSCALE))
-            break;
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            break;
-        if (arm_feature(env, ARM_FEATURE_V7)
-                && op1 == 2 && crm == 0 && op2 == 0) {
-            env->cp15.c0_cssel = val & 0xf;
-            break;
-        }
-        goto bad_reg;
-    case 1: /* System configuration.  */
-        if (arm_feature(env, ARM_FEATURE_V7)
-                && op1 == 0 && crm == 1 && op2 == 0) {
-            env->cp15.c1_scr = val;
-            break;
-        }
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            op2 = 0;
-        switch (op2) {
-        case 0:
-            if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
-                env->cp15.c1_sys = val;
-            /* ??? Lots of these bits are not implemented.  */
-            /* This may enable/disable the MMU, so do a TLB flush.  */
-            tlb_flush(env, 1);
-            break;
-        case 1: /* Auxiliary control register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE)) {
-                env->cp15.c1_xscaleauxcr = val;
-                break;
-            }
-            /* Not implemented.  */
-            break;
-        case 2:
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
-                goto bad_reg;
-            if (env->cp15.c1_coproc != val) {
-                env->cp15.c1_coproc = val;
-                /* ??? Is this safe when called from within a TB?  */
-                tb_flush(env);
-            }
-            break;
-        default:
-            goto bad_reg;
-        }
-        break;
-    case 4: /* Reserved.  */
-        goto bad_reg;
-    case 6: /* MMU Fault address / MPU base/size.  */
-        if (arm_feature(env, ARM_FEATURE_MPU)) {
-            if (crm >= 8)
-                goto bad_reg;
-            env->cp15.c6_region[crm] = val;
-        } else {
-            if (arm_feature(env, ARM_FEATURE_OMAPCP))
-                op2 = 0;
-            switch (op2) {
-            case 0:
-                env->cp15.c6_data = val;
-                break;
-            case 1: /* ??? This is WFAR on armv6 */
-            case 2:
-                env->cp15.c6_insn = val;
-                break;
-            default:
-                goto bad_reg;
-            }
-        }
-        break;
-    case 7: /* Cache control.  */
-        env->cp15.c15_i_max = 0x000;
-        env->cp15.c15_i_min = 0xff0;
-        if (op1 != 0) {
-            goto bad_reg;
-        }
-        /* No cache, so nothing to do except VA->PA translations. */
-        if (arm_feature(env, ARM_FEATURE_VAPA)) {
-            switch (crm) {
-            case 4:
-                if (arm_feature(env, ARM_FEATURE_V7)) {
-                    env->cp15.c7_par = val & 0xfffff6ff;
-                } else {
-                    env->cp15.c7_par = val & 0xfffff1ff;
-                }
-                break;
-            case 8: {
-                uint32_t phys_addr;
-                target_ulong page_size;
-                int prot;
-                int ret, is_user = op2 & 2;
-                int access_type = op2 & 1;
-
-                if (op2 & 4) {
-                    /* Other states are only available with TrustZone */
-                    goto bad_reg;
-                }
-                ret = get_phys_addr(env, val, access_type, is_user,
-                                    &phys_addr, &prot, &page_size);
-                if (ret == 0) {
-                    /* We do not set any attribute bits in the PAR */
-                    if (page_size == (1 << 24)
-                        && arm_feature(env, ARM_FEATURE_V7)) {
-                        env->cp15.c7_par = (phys_addr & 0xff000000) | 1 << 1;
-                    } else {
-                        env->cp15.c7_par = phys_addr & 0xfffff000;
-                    }
-                } else {
-                    env->cp15.c7_par = ((ret & (10 << 1)) >> 5) |
-                                       ((ret & (12 << 1)) >> 6) |
-                                       ((ret & 0xf) << 1) | 1;
-                }
-                break;
-            }
-            }
-        }
-        break;
-    case 9:
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            break;
-        if (arm_feature(env, ARM_FEATURE_STRONGARM))
-            break; /* Ignore ReadBuffer access */
-        switch (crm) {
-        case 0: /* Cache lockdown.  */
-           switch (op1) {
-           case 0: /* L1 cache.  */
-               switch (op2) {
-               case 0:
-                   env->cp15.c9_data = val;
-                   break;
-               case 1:
-                   env->cp15.c9_insn = val;
-                   break;
-               default:
-                   goto bad_reg;
-               }
-               break;
-           case 1: /* L2 cache.  */
-               /* Ignore writes to L2 lockdown/auxiliary registers.  */
-               break;
-           default:
-               goto bad_reg;
-           }
-           break;
-        case 1: /* TCM memory region registers.  */
-            /* Not implemented.  */
-            goto bad_reg;
-        default:
-            goto bad_reg;
-        }
-        break;
-    case 12: /* Reserved.  */
-        goto bad_reg;
-    }
-    return;
-bad_reg:
-    /* ??? For debugging only.  Should raise illegal instruction exception.  */
-    cpu_abort(env, "Unimplemented cp15 register write (c%d, c%d, {%d, %d})\n",
-              (insn >> 16) & 0xf, crm, op1, op2);
-}
-
-uint32_t HELPER(get_cp15)(CPUARMState *env, uint32_t insn)
-{
-    int op1;
-    int op2;
-    int crm;
-
-    op1 = (insn >> 21) & 7;
-    op2 = (insn >> 5) & 7;
-    crm = insn & 0xf;
-    switch ((insn >> 16) & 0xf) {
-    case 0: /* ID codes.  */
-        switch (op1) {
-        case 0:
-            switch (crm) {
-            case 0:
-                switch (op2) {
-                case 0: /* Device ID.  */
-                    return env->cp15.c0_cpuid;
-                case 1: /* Cache Type.  */
-                   return env->cp15.c0_cachetype;
-                case 2: /* TCM status.  */
-                    return 0;
-                case 3: /* TLB type register.  */
-                    return 0; /* No lockable TLB entries.  */
-                case 5: /* MPIDR */
-                    /* The MPIDR was standardised in v7; prior to
-                     * this it was implemented only in the 11MPCore.
-                     * For all other pre-v7 cores it does not exist.
-                     */
-                    if (arm_feature(env, ARM_FEATURE_V7) ||
-                        ARM_CPUID(env) == ARM_CPUID_ARM11MPCORE) {
-                        int mpidr = env->cpu_index;
-                        /* We don't support setting cluster ID ([8..11])
-                         * so these bits always RAZ.
-                         */
-                        if (arm_feature(env, ARM_FEATURE_V7MP)) {
-                            mpidr |= (1 << 31);
-                            /* Cores which are uniprocessor (non-coherent)
-                             * but still implement the MP extensions set
-                             * bit 30. (For instance, A9UP.) However we do
-                             * not currently model any of those cores.
-                             */
-                        }
-                        return mpidr;
-                    }
-                    /* otherwise fall through to the unimplemented-reg case */
-                default:
-                    goto bad_reg;
-                }
-            case 1:
-                if (!arm_feature(env, ARM_FEATURE_V6))
-                    goto bad_reg;
-                return env->cp15.c0_c1[op2];
-            case 2:
-                if (!arm_feature(env, ARM_FEATURE_V6))
-                    goto bad_reg;
-                return env->cp15.c0_c2[op2];
-            case 3: case 4: case 5: case 6: case 7:
-                return 0;
-            default:
-                goto bad_reg;
-            }
-        case 1:
-            /* These registers aren't documented on arm11 cores.  However
-               Linux looks at them anyway.  */
-            if (!arm_feature(env, ARM_FEATURE_V6))
-                goto bad_reg;
-            if (crm != 0)
-                goto bad_reg;
-            if (!arm_feature(env, ARM_FEATURE_V7))
-                return 0;
-
-            switch (op2) {
-            case 0:
-                return env->cp15.c0_ccsid[env->cp15.c0_cssel];
-            case 1:
-                return env->cp15.c0_clid;
-            case 7:
-                return 0;
-            }
-            goto bad_reg;
-        case 2:
-            if (op2 != 0 || crm != 0)
-                goto bad_reg;
-            return env->cp15.c0_cssel;
-        default:
-            goto bad_reg;
-        }
-    case 1: /* System configuration.  */
-        if (arm_feature(env, ARM_FEATURE_V7)
-            && op1 == 0 && crm == 1 && op2 == 0) {
-            return env->cp15.c1_scr;
-        }
-        if (arm_feature(env, ARM_FEATURE_OMAPCP))
-            op2 = 0;
-        switch (op2) {
-        case 0: /* Control register.  */
-            return env->cp15.c1_sys;
-        case 1: /* Auxiliary control register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
-                return env->cp15.c1_xscaleauxcr;
-            if (!arm_feature(env, ARM_FEATURE_AUXCR))
-                goto bad_reg;
-            switch (ARM_CPUID(env)) {
-            case ARM_CPUID_ARM1026:
-                return 1;
-            case ARM_CPUID_ARM1136:
-            case ARM_CPUID_ARM1136_R2:
-            case ARM_CPUID_ARM1176:
-                return 7;
-            case ARM_CPUID_ARM11MPCORE:
-                return 1;
-            case ARM_CPUID_CORTEXA8:
-                return 2;
-            case ARM_CPUID_CORTEXA9:
-            case ARM_CPUID_CORTEXA15:
-                return 0;
-            default:
-                goto bad_reg;
-            }
-        case 2: /* Coprocessor access register.  */
-            if (arm_feature(env, ARM_FEATURE_XSCALE))
-                goto bad_reg;
-            return env->cp15.c1_coproc;
-        default:
-            goto bad_reg;
-        }
-    case 4: /* Reserved.  */
-        goto bad_reg;
-    case 6: /* MMU Fault address.  */
-        if (arm_feature(env, ARM_FEATURE_MPU)) {
-            if (crm >= 8)
-                goto bad_reg;
-            return env->cp15.c6_region[crm];
-        } else {
-            if (arm_feature(env, ARM_FEATURE_OMAPCP))
-                op2 = 0;
-           switch (op2) {
-           case 0:
-               return env->cp15.c6_data;
-           case 1:
-               if (arm_feature(env, ARM_FEATURE_V6)) {
-                   /* Watchpoint Fault Adrress.  */
-                   return 0; /* Not implemented.  */
-               } else {
-                   /* Instruction Fault Adrress.  */
-                   /* Arm9 doesn't have an IFAR, but implementing it anyway
-                      shouldn't do any harm.  */
-                   return env->cp15.c6_insn;
-               }
-           case 2:
-               if (arm_feature(env, ARM_FEATURE_V6)) {
-                   /* Instruction Fault Adrress.  */
-                   return env->cp15.c6_insn;
-               } else {
-                   goto bad_reg;
-               }
-           default:
-               goto bad_reg;
-           }
-        }
-    case 7: /* Cache control.  */
-        if (crm == 4 && op1 == 0 && op2 == 0) {
-            return env->cp15.c7_par;
-        }
-        /* FIXME: Should only clear Z flag if destination is r15.  */
-        env->ZF = 0;
-        return 0;
-    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_data;
-                case 1:
-                    return env->cp15.c9_insn;
-                default:
-                    goto bad_reg;
-                }
-            case 1: /* L2 cache */
-                /* L2 Lockdown and Auxiliary control.  */
-                switch (op2) {
-                case 0:
-                    /* L2 cache lockdown (A8 only) */
-                    return 0;
-                case 2:
-                    /* L2 cache auxiliary control (A8) or control (A15) */
-                    if (ARM_CPUID(env) == ARM_CPUID_CORTEXA15) {
-                        /* Linux wants the number of processors from here.
-                         * Might as well set the interrupt-controller bit too.
-                         */
-                        return ((smp_cpus - 1) << 24) | (1 << 23);
-                    }
-                    return 0;
-                case 3:
-                    /* L2 cache extended control (A15) */
-                    return 0;
-                default:
-                    goto bad_reg;
-                }
-            default:
-                goto bad_reg;
-            }
-            break;
-        default:
-            goto bad_reg;
-        }
-        break;
-    case 11: /* TCM DMA control.  */
-    case 12: /* Reserved.  */
-        goto bad_reg;
-    }
-bad_reg:
-    /* ??? For debugging only.  Should raise illegal instruction exception.  */
-    cpu_abort(env, "Unimplemented cp15 register read (c%d, c%d, {%d, %d})\n",
-              (insn >> 16) & 0xf, crm, op1, op2);
-    return 0;
-}
-
 void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val)
 {
     if ((env->uncached_cpsr & CPSR_M) == mode) {
This page took 0.052865 seconds and 4 git commands to generate.