#include "cpu.h"
-#include "gdbstub.h"
+#include "exec/gdbstub.h"
#include "helper.h"
-#include "host-utils.h"
-#include "sysemu.h"
+#include "qemu/host-utils.h"
+#include "sysemu/sysemu.h"
+#include "qemu/bitops.h"
+
+#ifndef CONFIG_USER_ONLY
+static inline int get_phys_addr(CPUARMState *env, uint32_t address,
+ int access_type, int is_user,
+ hwaddr *phys_ptr, int *prot,
+ target_ulong *page_size);
+#endif
static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg)
{
return 0;
}
+static int tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Invalidate all (TLBIALL) */
+ tlb_flush(env, 1);
+ return 0;
+}
+
+static int tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */
+ tlb_flush_page(env, value & TARGET_PAGE_MASK);
+ return 0;
+}
+
+static int tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Invalidate by ASID (TLBIASID) */
+ tlb_flush(env, value == 0);
+ return 0;
+}
+
+static int tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */
+ tlb_flush_page(env, value & TARGET_PAGE_MASK);
+ return 0;
+}
+
static const ARMCPRegInfo cp_reginfo[] = {
/* DBGDIDR: just RAZ. In particular this means the "debug architecture
* version" bits will read as a reserved value, which should cause
*/
{ .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = CP_ANY,
.opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP },
+ /* MMU TLB control. Note that the wildcarding means we cover not just
+ * the unified TLB ops but also the dside/iside/inner-shareable variants.
+ */
+ { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY,
+ .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, },
+ { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY,
+ .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, },
+ { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY,
+ .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
};
*/
{ .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",
.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
};
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,
.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
};
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 */
+
+/* Return true if extended addresses are enabled, ie this is an
+ * LPAE implementation and we are using the long-descriptor translation
+ * table format because the TTBCR EAE bit is set.
+ */
+static inline bool extended_addresses_enabled(CPUARMState *env)
+{
+ return arm_feature(env, ARM_FEATURE_LPAE)
+ && (env->cp15.c2_control & (1 << 31));
+}
+
+static int ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
+{
+ hwaddr 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 (extended_addresses_enabled(env)) {
+ /* ret is a DFSR/IFSR value for the long descriptor
+ * translation table format, but with WnR always clear.
+ * Convert it to a 64-bit PAR.
+ */
+ uint64_t par64 = (1 << 11); /* LPAE bit always set */
+ if (ret == 0) {
+ par64 |= phys_addr & ~0xfffULL;
+ /* We don't set the ATTR or SH fields in the PAR. */
+ } else {
+ par64 |= 1; /* F */
+ par64 |= (ret & 0x3f) << 1; /* FS */
+ /* Note that S2WLK and FSTAGE are always zero, because we don't
+ * implement virtualization and therefore there can't be a stage 2
+ * fault.
+ */
+ }
+ env->cp15.c7_par = par64;
+ env->cp15.c7_par_hi = par64 >> 32;
+ } else {
+ /* ret is a DFSR/IFSR value for the short descriptor
+ * translation table format (with WnR always clear).
+ * Convert it to a 32-bit PAR.
+ */
+ 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)
{
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,
{ .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
};
static int vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
- value &= 7;
+ if (arm_feature(env, ARM_FEATURE_LPAE)) {
+ value &= ~((7 << 19) | (3 << 14) | (0xf << 3));
+ /* With LPAE the TTBCR could result in a change of ASID
+ * via the TTBCR.A1 bit, so do a TLB flush.
+ */
+ tlb_flush(env, 1);
+ } else {
+ value &= 7;
+ }
+ /* Note that we always calculate c2_mask and c2_base_mask, but
+ * they are only used for short-descriptor tables (ie if EAE is 0);
+ * for long-descriptor tables the TTBCR fields are used differently
+ * and the c2_mask and c2_base_mask values are meaningless.
+ */
env->cp15.c2_control = value;
env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> value);
env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> value);
.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
};
+static int omap_ticonfig_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ env->cp15.c15_ticonfig = value & 0xe7;
+ /* The OS_TYPE bit in this register changes the reported CPUID! */
+ env->cp15.c0_cpuid = (value & (1 << 5)) ?
+ ARM_CPUID_TI915T : ARM_CPUID_TI925T;
+ return 0;
+}
+
+static int omap_threadid_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ env->cp15.c15_threadid = value & 0xffff;
+ return 0;
+}
+
+static int omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ /* Wait-for-interrupt (deprecated) */
+ cpu_interrupt(env, CPU_INTERRUPT_HALT);
+ 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,
.fieldoffset = offsetof(CPUARMState, cp15.c5_data), .resetvalue = 0, },
+ { .name = "", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .type = ARM_CP_NOP },
+ { .name = "TICONFIG", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_ticonfig), .resetvalue = 0,
+ .writefn = omap_ticonfig_write },
+ { .name = "IMAX", .cp = 15, .crn = 15, .crm = 2, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_i_max), .resetvalue = 0, },
+ { .name = "IMIN", .cp = 15, .crn = 15, .crm = 3, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW, .resetvalue = 0xff0,
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_i_min) },
+ { .name = "THREADID", .cp = 15, .crn = 15, .crm = 4, .opc1 = 0, .opc2 = 0,
+ .access = PL1_RW,
+ .fieldoffset = offsetof(CPUARMState, cp15.c15_threadid), .resetvalue = 0,
+ .writefn = omap_threadid_write },
+ { .name = "TI925T_STATUS", .cp = 15, .crn = 15,
+ .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW,
+ .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, },
+ /* TODO: Peripheral port remap register:
+ * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller
+ * 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
+};
+
+static int xscale_cpar_write(CPUARMState *env, const ARMCPRegInfo *ri,
+ uint64_t value)
+{
+ value &= 0x3fff;
+ if (env->cp15.c15_cpar != value) {
+ /* Changes cp0 to cp13 behavior, so needs a TB flush. */
+ tb_flush(env);
+ env->cp15.c15_cpar = value;
+ }
+ return 0;
+}
+
+static const ARMCPRegInfo xscale_cp_reginfo[] = {
+ { .name = "XSCALE_CPAR",
+ .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
+};
+
+static const ARMCPRegInfo dummy_c15_cp_reginfo[] = {
+ /* RAZ/WI the whole crn=15 space, when we don't have a more specific
+ * implementation of this implementation-defined space.
+ * Ideally this should eventually disappear in favour of actually
+ * implementing the correct behaviour for all cores.
+ */
+ { .name = "C15_IMPDEF", .cp = 15, .crn = 15,
+ .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
+ .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 },
+ 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 the 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 */
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);
.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);
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)
return x;
}
-uint32_t HELPER(abs)(uint32_t x)
-{
- return ((int32_t)x < 0) ? -x : x;
-}
-
#if defined(CONFIG_USER_ONLY)
void do_interrupt (CPUARMState *env)
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)
{
case EXCP_BKPT:
if (semihosting_enabled) {
int nr;
- nr = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff;
+ nr = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
if (nr == 0xab) {
env->regs[15] += 2;
env->regs[0] = do_arm_semihosting(env);
if (semihosting_enabled) {
/* Check for semihosting interrupt. */
if (env->thumb) {
- mask = arm_lduw_code(env->regs[15] - 2, env->bswap_code) & 0xff;
+ mask = arm_lduw_code(env, env->regs[15] - 2, env->bswap_code)
+ & 0xff;
} else {
- mask = arm_ldl_code(env->regs[15] - 4, env->bswap_code)
+ mask = arm_ldl_code(env, env->regs[15] - 4, env->bswap_code)
& 0xffffff;
}
/* Only intercept calls from privileged modes, to provide some
case EXCP_BKPT:
/* See if this is a semihosting syscall. */
if (env->thumb && semihosting_enabled) {
- mask = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff;
+ mask = arm_lduw_code(env, env->regs[15], env->bswap_code) & 0xff;
if (mask == 0xab
&& (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
env->regs[15] += 2;
}
static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type,
- int is_user, uint32_t *phys_ptr, int *prot,
- target_ulong *page_size)
+ int is_user, hwaddr *phys_ptr,
+ int *prot, target_ulong *page_size)
{
int code;
uint32_t table;
int ap;
int domain;
int domain_prot;
- uint32_t phys_addr;
+ hwaddr phys_addr;
/* Pagetable walk. */
/* Lookup l1 descriptor. */
}
static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type,
- int is_user, uint32_t *phys_ptr, int *prot,
- target_ulong *page_size)
+ int is_user, hwaddr *phys_ptr,
+ int *prot, target_ulong *page_size)
{
int code;
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;
+ hwaddr phys_addr;
/* Pagetable walk. */
/* Lookup l1 descriptor. */
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);
}
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);
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;
return code | (domain << 4);
}
-static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, int access_type,
- int is_user, uint32_t *phys_ptr, int *prot)
+/* Fault type for long-descriptor MMU fault reporting; this corresponds
+ * to bits [5..2] in the STATUS field in long-format DFSR/IFSR.
+ */
+typedef enum {
+ translation_fault = 1,
+ access_fault = 2,
+ permission_fault = 3,
+} MMUFaultType;
+
+static int get_phys_addr_lpae(CPUARMState *env, uint32_t address,
+ int access_type, int is_user,
+ hwaddr *phys_ptr, int *prot,
+ target_ulong *page_size_ptr)
+{
+ /* Read an LPAE long-descriptor translation table. */
+ MMUFaultType fault_type = translation_fault;
+ uint32_t level = 1;
+ uint32_t epd;
+ uint32_t tsz;
+ uint64_t ttbr;
+ int ttbr_select;
+ int n;
+ hwaddr descaddr;
+ uint32_t tableattrs;
+ target_ulong page_size;
+ uint32_t attrs;
+
+ /* Determine whether this address is in the region controlled by
+ * TTBR0 or TTBR1 (or if it is in neither region and should fault).
+ * This is a Non-secure PL0/1 stage 1 translation, so controlled by
+ * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32:
+ */
+ uint32_t t0sz = extract32(env->cp15.c2_control, 0, 3);
+ uint32_t t1sz = extract32(env->cp15.c2_control, 16, 3);
+ if (t0sz && !extract32(address, 32 - t0sz, t0sz)) {
+ /* there is a ttbr0 region and we are in it (high bits all zero) */
+ ttbr_select = 0;
+ } else if (t1sz && !extract32(~address, 32 - t1sz, t1sz)) {
+ /* there is a ttbr1 region and we are in it (high bits all one) */
+ ttbr_select = 1;
+ } else if (!t0sz) {
+ /* ttbr0 region is "everything not in the ttbr1 region" */
+ ttbr_select = 0;
+ } else if (!t1sz) {
+ /* ttbr1 region is "everything not in the ttbr0 region" */
+ ttbr_select = 1;
+ } else {
+ /* in the gap between the two regions, this is a Translation fault */
+ fault_type = translation_fault;
+ goto do_fault;
+ }
+
+ /* Note that QEMU ignores shareability and cacheability attributes,
+ * so we don't need to do anything with the SH, ORGN, IRGN fields
+ * in the TTBCR. Similarly, TTBCR:A1 selects whether we get the
+ * ASID from TTBR0 or TTBR1, but QEMU's TLB doesn't currently
+ * implement any ASID-like capability so we can ignore it (instead
+ * we will always flush the TLB any time the ASID is changed).
+ */
+ if (ttbr_select == 0) {
+ ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
+ epd = extract32(env->cp15.c2_control, 7, 1);
+ tsz = t0sz;
+ } else {
+ ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
+ epd = extract32(env->cp15.c2_control, 23, 1);
+ tsz = t1sz;
+ }
+
+ if (epd) {
+ /* Translation table walk disabled => Translation fault on TLB miss */
+ goto do_fault;
+ }
+
+ /* If the region is small enough we will skip straight to a 2nd level
+ * lookup. This affects the number of bits of the address used in
+ * combination with the TTBR to find the first descriptor. ('n' here
+ * matches the usage in the ARM ARM sB3.6.6, where bits [39..n] are
+ * from the TTBR, [n-1..3] from the vaddr, and [2..0] always zero).
+ */
+ if (tsz > 1) {
+ level = 2;
+ n = 14 - tsz;
+ } else {
+ n = 5 - tsz;
+ }
+
+ /* Clear the vaddr bits which aren't part of the within-region address,
+ * so that we don't have to special case things when calculating the
+ * first descriptor address.
+ */
+ address &= (0xffffffffU >> tsz);
+
+ /* Now we can extract the actual base address from the TTBR */
+ descaddr = extract64(ttbr, 0, 40);
+ descaddr &= ~((1ULL << n) - 1);
+
+ tableattrs = 0;
+ for (;;) {
+ uint64_t descriptor;
+
+ descaddr |= ((address >> (9 * (4 - level))) & 0xff8);
+ descriptor = ldq_phys(descaddr);
+ if (!(descriptor & 1) ||
+ (!(descriptor & 2) && (level == 3))) {
+ /* Invalid, or the Reserved level 3 encoding */
+ goto do_fault;
+ }
+ descaddr = descriptor & 0xfffffff000ULL;
+
+ if ((descriptor & 2) && (level < 3)) {
+ /* Table entry. The top five bits are attributes which may
+ * propagate down through lower levels of the table (and
+ * which are all arranged so that 0 means "no effect", so
+ * we can gather them up by ORing in the bits at each level).
+ */
+ tableattrs |= extract64(descriptor, 59, 5);
+ level++;
+ continue;
+ }
+ /* Block entry at level 1 or 2, or page entry at level 3.
+ * These are basically the same thing, although the number
+ * of bits we pull in from the vaddr varies.
+ */
+ page_size = (1 << (39 - (9 * level)));
+ descaddr |= (address & (page_size - 1));
+ /* Extract attributes from the descriptor and merge with table attrs */
+ attrs = extract64(descriptor, 2, 10)
+ | (extract64(descriptor, 52, 12) << 10);
+ attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */
+ attrs |= extract32(tableattrs, 3, 1) << 5; /* APTable[1] => AP[2] */
+ /* The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1
+ * means "force PL1 access only", which means forcing AP[1] to 0.
+ */
+ if (extract32(tableattrs, 2, 1)) {
+ attrs &= ~(1 << 4);
+ }
+ /* Since we're always in the Non-secure state, NSTable is ignored. */
+ break;
+ }
+ /* Here descaddr is the final physical address, and attributes
+ * are all in attrs.
+ */
+ fault_type = access_fault;
+ if ((attrs & (1 << 8)) == 0) {
+ /* Access flag */
+ goto do_fault;
+ }
+ fault_type = permission_fault;
+ if (is_user && !(attrs & (1 << 4))) {
+ /* Unprivileged access not enabled */
+ goto do_fault;
+ }
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ if (attrs & (1 << 12) || (!is_user && (attrs & (1 << 11)))) {
+ /* XN or PXN */
+ if (access_type == 2) {
+ goto do_fault;
+ }
+ *prot &= ~PAGE_EXEC;
+ }
+ if (attrs & (1 << 5)) {
+ /* Write access forbidden */
+ if (access_type == 1) {
+ goto do_fault;
+ }
+ *prot &= ~PAGE_WRITE;
+ }
+
+ *phys_ptr = descaddr;
+ *page_size_ptr = page_size;
+ return 0;
+
+do_fault:
+ /* Long-descriptor format IFSR/DFSR value */
+ return (1 << 9) | (fault_type << 2) | level;
+}
+
+static int get_phys_addr_mpu(CPUARMState *env, uint32_t address,
+ int access_type, int is_user,
+ hwaddr *phys_ptr, int *prot)
{
int n;
uint32_t mask;
return 0;
}
+/* get_phys_addr - get the physical address for this virtual address
+ *
+ * Find the physical address corresponding to the given virtual address,
+ * by doing a translation table walk on MMU based systems or using the
+ * MPU state on MPU based systems.
+ *
+ * Returns 0 if the translation was successful. Otherwise, phys_ptr,
+ * prot and page_size are not filled in, and the return value provides
+ * information on why the translation aborted, in the format of a
+ * DFSR/IFSR fault register, with the following caveats:
+ * * we honour the short vs long DFSR format differences.
+ * * the WnR bit is never set (the caller must do this).
+ * * for MPU based systems we don't bother to return a full FSR format
+ * value.
+ *
+ * @env: CPUARMState
+ * @address: virtual address to get physical address for
+ * @access_type: 0 for read, 1 for write, 2 for execute
+ * @is_user: 0 for privileged access, 1 for user
+ * @phys_ptr: set to the physical address corresponding to the virtual address
+ * @prot: set to the permissions for the page containing phys_ptr
+ * @page_size: set to the size of the page containing phys_ptr
+ */
static inline int get_phys_addr(CPUARMState *env, uint32_t address,
int access_type, int is_user,
- uint32_t *phys_ptr, int *prot,
+ hwaddr *phys_ptr, int *prot,
target_ulong *page_size)
{
/* Fast Context Switch Extension. */
*page_size = TARGET_PAGE_SIZE;
return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr,
prot);
+ } else if (extended_addresses_enabled(env)) {
+ return get_phys_addr_lpae(env, address, access_type, is_user, phys_ptr,
+ prot, page_size);
} else if (env->cp15.c1_sys & (1 << 23)) {
return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr,
prot, page_size);
int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address,
int access_type, int mmu_idx)
{
- uint32_t phys_addr;
+ hwaddr phys_addr;
target_ulong page_size;
int prot;
int ret, is_user;
&page_size);
if (ret == 0) {
/* Map a single [sub]page. */
- phys_addr &= ~(uint32_t)0x3ff;
+ phys_addr &= ~(hwaddr)0x3ff;
address &= ~(uint32_t)0x3ff;
tlb_set_page (env, address, phys_addr, prot, mmu_idx, page_size);
return 0;
return 1;
}
-target_phys_addr_t cpu_get_phys_page_debug(CPUARMState *env, target_ulong addr)
+hwaddr cpu_get_phys_page_debug(CPUARMState *env, target_ulong addr)
{
- uint32_t phys_addr;
+ hwaddr phys_addr;
target_ulong page_size;
int prot;
int ret;
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 8: /* MMU TLB control. */
- switch (op2) {
- case 0: /* Invalidate all (TLBIALL) */
- tlb_flush(env, 1);
- break;
- case 1: /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */
- tlb_flush_page(env, val & TARGET_PAGE_MASK);
- break;
- case 2: /* Invalidate by ASID (TLBIASID) */
- tlb_flush(env, val == 0);
- break;
- case 3: /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */
- tlb_flush_page(env, val & TARGET_PAGE_MASK);
- break;
- default:
- goto bad_reg;
- }
- 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;
- case 15: /* Implementation specific. */
- if (arm_feature(env, ARM_FEATURE_XSCALE)) {
- if (op2 == 0 && crm == 1) {
- if (env->cp15.c15_cpar != (val & 0x3fff)) {
- /* Changes cp0 to cp13 behavior, so needs a TB flush. */
- tb_flush(env);
- env->cp15.c15_cpar = val & 0x3fff;
- }
- break;
- }
- goto bad_reg;
- }
- if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
- switch (crm) {
- case 0:
- break;
- case 1: /* Set TI925T configuration. */
- env->cp15.c15_ticonfig = val & 0xe7;
- env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */
- ARM_CPUID_TI915T : ARM_CPUID_TI925T;
- break;
- case 2: /* Set I_max. */
- env->cp15.c15_i_max = val;
- break;
- case 3: /* Set I_min. */
- env->cp15.c15_i_min = val;
- break;
- case 4: /* Set thread-ID. */
- env->cp15.c15_threadid = val & 0xffff;
- break;
- case 8: /* Wait-for-interrupt (deprecated). */
- cpu_interrupt(env, CPU_INTERRUPT_HALT);
- break;
- default:
- goto bad_reg;
- }
- }
- if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) {
- switch (crm) {
- case 0:
- if ((op1 == 0) && (op2 == 0)) {
- env->cp15.c15_power_control = val;
- } else if ((op1 == 0) && (op2 == 1)) {
- env->cp15.c15_diagnostic = val;
- } else if ((op1 == 0) && (op2 == 2)) {
- env->cp15.c15_power_diagnostic = val;
- }
- default:
- break;
- }
- }
- break;
- }
- 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 8: /* MMU TLB control. */
- goto bad_reg;
- 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;
- case 15: /* Implementation specific. */
- if (arm_feature(env, ARM_FEATURE_XSCALE)) {
- if (op2 == 0 && crm == 1)
- return env->cp15.c15_cpar;
-
- goto bad_reg;
- }
- if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
- switch (crm) {
- case 0:
- return 0;
- case 1: /* Read TI925T configuration. */
- return env->cp15.c15_ticonfig;
- case 2: /* Read I_max. */
- return env->cp15.c15_i_max;
- case 3: /* Read I_min. */
- return env->cp15.c15_i_min;
- case 4: /* Read thread-ID. */
- return env->cp15.c15_threadid;
- case 8: /* TI925T_status */
- return 0;
- }
- /* TODO: Peripheral port remap register:
- * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt
- * controller base address at $rn & ~0xfff and map size of
- * 0x200 << ($rn & 0xfff), when MMU is off. */
- goto bad_reg;
- }
- if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) {
- switch (crm) {
- case 0:
- if ((op1 == 4) && (op2 == 0)) {
- /* The config_base_address should hold the value of
- * the peripheral base. ARM should get this from a CPU
- * object property, but that support isn't available in
- * December 2011. Default to 0 for now and board models
- * that care can set it by a private hook */
- return env->cp15.c15_config_base_address;
- } else if ((op1 == 0) && (op2 == 0)) {
- /* power_control should be set to maximum latency. Again,
- default to 0 and set by private hook */
- return env->cp15.c15_power_control;
- } else if ((op1 == 0) && (op2 == 1)) {
- return env->cp15.c15_diagnostic;
- } else if ((op1 == 0) && (op2 == 2)) {
- return env->cp15.c15_power_diagnostic;
- }
- break;
- case 1: /* NEON Busy */
- return 0;
- case 5: /* tlb lockdown */
- case 6:
- case 7:
- if ((op1 == 5) && (op2 == 2)) {
- return 0;
- }
- break;
- default:
- break;
- }
- goto bad_reg;
- }
- return 0;
- }
-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) {
return (val >> 32) | (val != 0);
}
-/* VFP support. We follow the convention used for VFP instrunctions:
- Single precition routines have a "s" suffix, double precision a
+/* VFP support. We follow the convention used for VFP instructions:
+ Single precision routines have a "s" suffix, double precision a
"d" suffix. */
/* Convert host exception flags to vfp form. */