| /* |
| * ARM generic helpers. |
| * |
| * This code is licensed under the GNU GPL v2 or later. |
| * |
| * SPDX-License-Identifier: GPL-2.0-or-later |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/units.h" |
| #include "target/arm/idau.h" |
| #include "trace.h" |
| #include "cpu.h" |
| #include "internals.h" |
| #include "exec/gdbstub.h" |
| #include "exec/helper-proto.h" |
| #include "qemu/host-utils.h" |
| #include "qemu/main-loop.h" |
| #include "qemu/bitops.h" |
| #include "qemu/crc32c.h" |
| #include "qemu/qemu-print.h" |
| #include "exec/exec-all.h" |
| #include <zlib.h> /* For crc32 */ |
| #include "hw/irq.h" |
| #include "hw/semihosting/semihost.h" |
| #include "sysemu/cpus.h" |
| #include "sysemu/kvm.h" |
| #include "sysemu/tcg.h" |
| #include "qemu/range.h" |
| #include "qapi/qapi-commands-machine-target.h" |
| #include "qapi/error.h" |
| #include "qemu/guest-random.h" |
| #ifdef CONFIG_TCG |
| #include "arm_ldst.h" |
| #include "exec/cpu_ldst.h" |
| #endif |
| |
| #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ |
| |
| #ifndef CONFIG_USER_ONLY |
| |
| static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, |
| MMUAccessType access_type, ARMMMUIdx mmu_idx, |
| hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, |
| target_ulong *page_size_ptr, |
| ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs); |
| #endif |
| |
| static void switch_mode(CPUARMState *env, int mode); |
| |
| static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; |
| |
| /* VFP data registers are always little-endian. */ |
| if (reg < nregs) { |
| return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); |
| } |
| if (arm_feature(env, ARM_FEATURE_NEON)) { |
| /* Aliases for Q regs. */ |
| nregs += 16; |
| if (reg < nregs) { |
| uint64_t *q = aa32_vfp_qreg(env, reg - 32); |
| return gdb_get_reg128(buf, q[0], q[1]); |
| } |
| } |
| switch (reg - nregs) { |
| case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); break; |
| case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); break; |
| case 2: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); break; |
| } |
| return 0; |
| } |
| |
| static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; |
| |
| if (reg < nregs) { |
| *aa32_vfp_dreg(env, reg) = ldq_le_p(buf); |
| return 8; |
| } |
| if (arm_feature(env, ARM_FEATURE_NEON)) { |
| nregs += 16; |
| if (reg < nregs) { |
| uint64_t *q = aa32_vfp_qreg(env, reg - 32); |
| q[0] = ldq_le_p(buf); |
| q[1] = ldq_le_p(buf + 8); |
| return 16; |
| } |
| } |
| switch (reg - nregs) { |
| case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4; |
| case 1: vfp_set_fpscr(env, ldl_p(buf)); return 4; |
| case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); return 4; |
| } |
| return 0; |
| } |
| |
| static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) |
| { |
| switch (reg) { |
| case 0 ... 31: |
| { |
| /* 128 bit FP register - quads are in LE order */ |
| uint64_t *q = aa64_vfp_qreg(env, reg); |
| return gdb_get_reg128(buf, q[1], q[0]); |
| } |
| case 32: |
| /* FPSR */ |
| return gdb_get_reg32(buf, vfp_get_fpsr(env)); |
| case 33: |
| /* FPCR */ |
| return gdb_get_reg32(buf,vfp_get_fpcr(env)); |
| default: |
| return 0; |
| } |
| } |
| |
| static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| switch (reg) { |
| case 0 ... 31: |
| /* 128 bit FP register */ |
| { |
| uint64_t *q = aa64_vfp_qreg(env, reg); |
| q[0] = ldq_le_p(buf); |
| q[1] = ldq_le_p(buf + 8); |
| return 16; |
| } |
| case 32: |
| /* FPSR */ |
| vfp_set_fpsr(env, ldl_p(buf)); |
| return 4; |
| case 33: |
| /* FPCR */ |
| vfp_set_fpcr(env, ldl_p(buf)); |
| return 4; |
| default: |
| return 0; |
| } |
| } |
| |
| static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| assert(ri->fieldoffset); |
| if (cpreg_field_is_64bit(ri)) { |
| return CPREG_FIELD64(env, ri); |
| } else { |
| return CPREG_FIELD32(env, ri); |
| } |
| } |
| |
| static void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| assert(ri->fieldoffset); |
| if (cpreg_field_is_64bit(ri)) { |
| CPREG_FIELD64(env, ri) = value; |
| } else { |
| CPREG_FIELD32(env, ri) = value; |
| } |
| } |
| |
| static void *raw_ptr(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return (char *)env + ri->fieldoffset; |
| } |
| |
| uint64_t read_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| /* Raw read of a coprocessor register (as needed for migration, etc). */ |
| if (ri->type & ARM_CP_CONST) { |
| return ri->resetvalue; |
| } else if (ri->raw_readfn) { |
| return ri->raw_readfn(env, ri); |
| } else if (ri->readfn) { |
| return ri->readfn(env, ri); |
| } else { |
| return raw_read(env, ri); |
| } |
| } |
| |
| static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t v) |
| { |
| /* Raw write of a coprocessor register (as needed for migration, etc). |
| * Note that constant registers are treated as write-ignored; the |
| * caller should check for success by whether a readback gives the |
| * value written. |
| */ |
| if (ri->type & ARM_CP_CONST) { |
| return; |
| } else if (ri->raw_writefn) { |
| ri->raw_writefn(env, ri, v); |
| } else if (ri->writefn) { |
| ri->writefn(env, ri, v); |
| } else { |
| raw_write(env, ri, v); |
| } |
| } |
| |
| /** |
| * arm_get/set_gdb_*: get/set a gdb register |
| * @env: the CPU state |
| * @buf: a buffer to copy to/from |
| * @reg: register number (offset from start of group) |
| * |
| * We return the number of bytes copied |
| */ |
| |
| static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| const ARMCPRegInfo *ri; |
| uint32_t key; |
| |
| key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; |
| ri = get_arm_cp_reginfo(cpu->cp_regs, key); |
| if (ri) { |
| if (cpreg_field_is_64bit(ri)) { |
| return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); |
| } else { |
| return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); |
| } |
| } |
| return 0; |
| } |
| |
| static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| return 0; |
| } |
| |
| #ifdef TARGET_AARCH64 |
| static int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| switch (reg) { |
| /* The first 32 registers are the zregs */ |
| case 0 ... 31: |
| { |
| int vq, len = 0; |
| for (vq = 0; vq < cpu->sve_max_vq; vq++) { |
| len += gdb_get_reg128(buf, |
| env->vfp.zregs[reg].d[vq * 2 + 1], |
| env->vfp.zregs[reg].d[vq * 2]); |
| } |
| return len; |
| } |
| case 32: |
| return gdb_get_reg32(buf, vfp_get_fpsr(env)); |
| case 33: |
| return gdb_get_reg32(buf, vfp_get_fpcr(env)); |
| /* then 16 predicates and the ffr */ |
| case 34 ... 50: |
| { |
| int preg = reg - 34; |
| int vq, len = 0; |
| for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { |
| len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); |
| } |
| return len; |
| } |
| case 51: |
| { |
| /* |
| * We report in Vector Granules (VG) which is 64bit in a Z reg |
| * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. |
| */ |
| int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; |
| return gdb_get_reg32(buf, vq * 2); |
| } |
| default: |
| /* gdbstub asked for something out our range */ |
| qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| /* The first 32 registers are the zregs */ |
| switch (reg) { |
| /* The first 32 registers are the zregs */ |
| case 0 ... 31: |
| { |
| int vq, len = 0; |
| uint64_t *p = (uint64_t *) buf; |
| for (vq = 0; vq < cpu->sve_max_vq; vq++) { |
| env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; |
| env->vfp.zregs[reg].d[vq * 2] = *p++; |
| len += 16; |
| } |
| return len; |
| } |
| case 32: |
| vfp_set_fpsr(env, *(uint32_t *)buf); |
| return 4; |
| case 33: |
| vfp_set_fpcr(env, *(uint32_t *)buf); |
| return 4; |
| case 34 ... 50: |
| { |
| int preg = reg - 34; |
| int vq, len = 0; |
| uint64_t *p = (uint64_t *) buf; |
| for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { |
| env->vfp.pregs[preg].p[vq / 4] = *p++; |
| len += 8; |
| } |
| return len; |
| } |
| case 51: |
| /* cannot set vg via gdbstub */ |
| return 0; |
| default: |
| /* gdbstub asked for something out our range */ |
| break; |
| } |
| |
| return 0; |
| } |
| #endif /* TARGET_AARCH64 */ |
| |
| static bool raw_accessors_invalid(const ARMCPRegInfo *ri) |
| { |
| /* Return true if the regdef would cause an assertion if you called |
| * read_raw_cp_reg() or write_raw_cp_reg() on it (ie if it is a |
| * program bug for it not to have the NO_RAW flag). |
| * NB that returning false here doesn't necessarily mean that calling |
| * read/write_raw_cp_reg() is safe, because we can't distinguish "has |
| * read/write access functions which are safe for raw use" from "has |
| * read/write access functions which have side effects but has forgotten |
| * to provide raw access functions". |
| * The tests here line up with the conditions in read/write_raw_cp_reg() |
| * and assertions in raw_read()/raw_write(). |
| */ |
| if ((ri->type & ARM_CP_CONST) || |
| ri->fieldoffset || |
| ((ri->raw_writefn || ri->writefn) && (ri->raw_readfn || ri->readfn))) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync) |
| { |
| /* Write the coprocessor state from cpu->env to the (index,value) list. */ |
| int i; |
| bool ok = true; |
| |
| for (i = 0; i < cpu->cpreg_array_len; i++) { |
| uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); |
| const ARMCPRegInfo *ri; |
| uint64_t newval; |
| |
| ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); |
| if (!ri) { |
| ok = false; |
| continue; |
| } |
| if (ri->type & ARM_CP_NO_RAW) { |
| continue; |
| } |
| |
| newval = read_raw_cp_reg(&cpu->env, ri); |
| if (kvm_sync) { |
| /* |
| * Only sync if the previous list->cpustate sync succeeded. |
| * Rather than tracking the success/failure state for every |
| * item in the list, we just recheck "does the raw write we must |
| * have made in write_list_to_cpustate() read back OK" here. |
| */ |
| uint64_t oldval = cpu->cpreg_values[i]; |
| |
| if (oldval == newval) { |
| continue; |
| } |
| |
| write_raw_cp_reg(&cpu->env, ri, oldval); |
| if (read_raw_cp_reg(&cpu->env, ri) != oldval) { |
| continue; |
| } |
| |
| write_raw_cp_reg(&cpu->env, ri, newval); |
| } |
| cpu->cpreg_values[i] = newval; |
| } |
| return ok; |
| } |
| |
| bool write_list_to_cpustate(ARMCPU *cpu) |
| { |
| int i; |
| bool ok = true; |
| |
| for (i = 0; i < cpu->cpreg_array_len; i++) { |
| uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); |
| uint64_t v = cpu->cpreg_values[i]; |
| const ARMCPRegInfo *ri; |
| |
| ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); |
| if (!ri) { |
| ok = false; |
| continue; |
| } |
| if (ri->type & ARM_CP_NO_RAW) { |
| continue; |
| } |
| /* Write value and confirm it reads back as written |
| * (to catch read-only registers and partially read-only |
| * registers where the incoming migration value doesn't match) |
| */ |
| write_raw_cp_reg(&cpu->env, ri, v); |
| if (read_raw_cp_reg(&cpu->env, ri) != v) { |
| ok = false; |
| } |
| } |
| return ok; |
| } |
| |
| static void add_cpreg_to_list(gpointer key, gpointer opaque) |
| { |
| ARMCPU *cpu = opaque; |
| uint64_t regidx; |
| const ARMCPRegInfo *ri; |
| |
| regidx = *(uint32_t *)key; |
| ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); |
| |
| if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { |
| cpu->cpreg_indexes[cpu->cpreg_array_len] = cpreg_to_kvm_id(regidx); |
| /* The value array need not be initialized at this point */ |
| cpu->cpreg_array_len++; |
| } |
| } |
| |
| static void count_cpreg(gpointer key, gpointer opaque) |
| { |
| ARMCPU *cpu = opaque; |
| uint64_t regidx; |
| const ARMCPRegInfo *ri; |
| |
| regidx = *(uint32_t *)key; |
| ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); |
| |
| if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { |
| cpu->cpreg_array_len++; |
| } |
| } |
| |
| static gint cpreg_key_compare(gconstpointer a, gconstpointer b) |
| { |
| uint64_t aidx = cpreg_to_kvm_id(*(uint32_t *)a); |
| uint64_t bidx = cpreg_to_kvm_id(*(uint32_t *)b); |
| |
| if (aidx > bidx) { |
| return 1; |
| } |
| if (aidx < bidx) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| void init_cpreg_list(ARMCPU *cpu) |
| { |
| /* Initialise the cpreg_tuples[] array based on the cp_regs hash. |
| * Note that we require cpreg_tuples[] to be sorted by key ID. |
| */ |
| GList *keys; |
| int arraylen; |
| |
| keys = g_hash_table_get_keys(cpu->cp_regs); |
| keys = g_list_sort(keys, cpreg_key_compare); |
| |
| cpu->cpreg_array_len = 0; |
| |
| g_list_foreach(keys, count_cpreg, cpu); |
| |
| arraylen = cpu->cpreg_array_len; |
| cpu->cpreg_indexes = g_new(uint64_t, arraylen); |
| cpu->cpreg_values = g_new(uint64_t, arraylen); |
| cpu->cpreg_vmstate_indexes = g_new(uint64_t, arraylen); |
| cpu->cpreg_vmstate_values = g_new(uint64_t, arraylen); |
| cpu->cpreg_vmstate_array_len = cpu->cpreg_array_len; |
| cpu->cpreg_array_len = 0; |
| |
| g_list_foreach(keys, add_cpreg_to_list, cpu); |
| |
| assert(cpu->cpreg_array_len == arraylen); |
| |
| g_list_free(keys); |
| } |
| |
| /* |
| * Some registers are not accessible if EL3.NS=0 and EL3 is using AArch32 but |
| * they are accessible when EL3 is using AArch64 regardless of EL3.NS. |
| * |
| * access_el3_aa32ns: Used to check AArch32 register views. |
| * access_el3_aa32ns_aa64any: Used to check both AArch32/64 register views. |
| */ |
| static CPAccessResult access_el3_aa32ns(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| bool secure = arm_is_secure_below_el3(env); |
| |
| assert(!arm_el_is_aa64(env, 3)); |
| if (secure) { |
| return CP_ACCESS_TRAP_UNCATEGORIZED; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult access_el3_aa32ns_aa64any(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (!arm_el_is_aa64(env, 3)) { |
| return access_el3_aa32ns(env, ri, isread); |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Some secure-only AArch32 registers trap to EL3 if used from |
| * Secure EL1 (but are just ordinary UNDEF in other non-EL3 contexts). |
| * Note that an access from Secure EL1 can only happen if EL3 is AArch64. |
| * We assume that the .access field is set to PL1_RW. |
| */ |
| static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 3) { |
| return CP_ACCESS_OK; |
| } |
| if (arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| /* This will be EL1 NS and EL2 NS, which just UNDEF */ |
| return CP_ACCESS_TRAP_UNCATEGORIZED; |
| } |
| |
| /* Check for traps to "powerdown debug" registers, which are controlled |
| * by MDCR.TDOSA |
| */ |
| static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| int el = arm_current_el(env); |
| bool mdcr_el2_tdosa = (env->cp15.mdcr_el2 & MDCR_TDOSA) || |
| (env->cp15.mdcr_el2 & MDCR_TDE) || |
| (arm_hcr_el2_eff(env) & HCR_TGE); |
| |
| if (el < 2 && mdcr_el2_tdosa && !arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps to "debug ROM" registers, which are controlled |
| * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. |
| */ |
| static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| int el = arm_current_el(env); |
| bool mdcr_el2_tdra = (env->cp15.mdcr_el2 & MDCR_TDRA) || |
| (env->cp15.mdcr_el2 & MDCR_TDE) || |
| (arm_hcr_el2_eff(env) & HCR_TGE); |
| |
| if (el < 2 && mdcr_el2_tdra && !arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps to general debug registers, which are controlled |
| * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. |
| */ |
| static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| int el = arm_current_el(env); |
| bool mdcr_el2_tda = (env->cp15.mdcr_el2 & MDCR_TDA) || |
| (env->cp15.mdcr_el2 & MDCR_TDE) || |
| (arm_hcr_el2_eff(env) & HCR_TGE); |
| |
| if (el < 2 && mdcr_el2_tda && !arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps to performance monitor registers, which are controlled |
| * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. |
| */ |
| static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| int el = arm_current_el(env); |
| |
| if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM) |
| && !arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps from EL1 due to HCR_EL2.TVM and HCR_EL2.TRVM. */ |
| static CPAccessResult access_tvm_trvm(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1) { |
| uint64_t trap = isread ? HCR_TRVM : HCR_TVM; |
| if (arm_hcr_el2_eff(env) & trap) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps from EL1 due to HCR_EL2.TSW. */ |
| static CPAccessResult access_tsw(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TSW)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps from EL1 due to HCR_EL2.TACR. */ |
| static CPAccessResult access_tacr(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TACR)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| /* Check for traps from EL1 due to HCR_EL2.TTLB. */ |
| static CPAccessResult access_ttlb(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TTLB)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| raw_write(env, ri, value); |
| tlb_flush(CPU(cpu)); /* Flush TLB as domain not tracked in TLB */ |
| } |
| |
| static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| if (raw_read(env, ri) != value) { |
| /* Unlike real hardware the qemu TLB uses virtual addresses, |
| * not modified virtual addresses, so this causes a TLB flush. |
| */ |
| tlb_flush(CPU(cpu)); |
| raw_write(env, ri, value); |
| } |
| } |
| |
| static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_PMSA) |
| && !extended_addresses_enabled(env)) { |
| /* For VMSA (when not using the LPAE long descriptor page table |
| * format) this register includes the ASID, so do a TLB flush. |
| * For PMSA it is purely a process ID and no action is needed. |
| */ |
| tlb_flush(CPU(cpu)); |
| } |
| raw_write(env, ri, value); |
| } |
| |
| /* IS variants of TLB operations must affect all cores */ |
| static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_all_cpus_synced(cs); |
| } |
| |
| static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_all_cpus_synced(cs); |
| } |
| |
| static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); |
| } |
| |
| static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); |
| } |
| |
| /* |
| * Non-IS variants of TLB operations are upgraded to |
| * IS versions if we are at NS EL1 and HCR_EL2.FB is set to |
| * force broadcast of these operations. |
| */ |
| static bool tlb_force_broadcast(CPUARMState *env) |
| { |
| return (env->cp15.hcr_el2 & HCR_FB) && |
| arm_current_el(env) == 1 && arm_is_secure_below_el3(env); |
| } |
| |
| static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate all (TLBIALL) */ |
| CPUState *cs = env_cpu(env); |
| |
| if (tlb_force_broadcast(env)) { |
| tlb_flush_all_cpus_synced(cs); |
| } else { |
| tlb_flush(cs); |
| } |
| } |
| |
| static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ |
| CPUState *cs = env_cpu(env); |
| |
| value &= TARGET_PAGE_MASK; |
| if (tlb_force_broadcast(env)) { |
| tlb_flush_page_all_cpus_synced(cs, value); |
| } else { |
| tlb_flush_page(cs, value); |
| } |
| } |
| |
| static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate by ASID (TLBIASID) */ |
| CPUState *cs = env_cpu(env); |
| |
| if (tlb_force_broadcast(env)) { |
| tlb_flush_all_cpus_synced(cs); |
| } else { |
| tlb_flush(cs); |
| } |
| } |
| |
| static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ |
| CPUState *cs = env_cpu(env); |
| |
| value &= TARGET_PAGE_MASK; |
| if (tlb_force_broadcast(env)) { |
| tlb_flush_page_all_cpus_synced(cs, value); |
| } else { |
| tlb_flush_page(cs, value); |
| } |
| } |
| |
| static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_by_mmuidx(cs, |
| ARMMMUIdxBit_E10_1 | |
| ARMMMUIdxBit_E10_1_PAN | |
| ARMMMUIdxBit_E10_0 | |
| ARMMMUIdxBit_Stage2); |
| } |
| |
| static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_by_mmuidx_all_cpus_synced(cs, |
| ARMMMUIdxBit_E10_1 | |
| ARMMMUIdxBit_E10_1_PAN | |
| ARMMMUIdxBit_E10_0 | |
| ARMMMUIdxBit_Stage2); |
| } |
| |
| static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate by IPA. This has to invalidate any structures that |
| * contain only stage 2 translation information, but does not need |
| * to apply to structures that contain combined stage 1 and stage 2 |
| * translation information. |
| * This must NOP if EL2 isn't implemented or SCR_EL3.NS is zero. |
| */ |
| CPUState *cs = env_cpu(env); |
| uint64_t pageaddr; |
| |
| if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) { |
| return; |
| } |
| |
| pageaddr = sextract64(value << 12, 0, 40); |
| |
| tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_Stage2); |
| } |
| |
| static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| uint64_t pageaddr; |
| |
| if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) { |
| return; |
| } |
| |
| pageaddr = sextract64(value << 12, 0, 40); |
| |
| tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, |
| ARMMMUIdxBit_Stage2); |
| } |
| |
| static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_by_mmuidx(cs, ARMMMUIdxBit_E2); |
| } |
| |
| static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| |
| tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_E2); |
| } |
| |
| static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); |
| |
| tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdxBit_E2); |
| } |
| |
| static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = env_cpu(env); |
| uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); |
| |
| tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr, |
| ARMMMUIdxBit_E2); |
| } |
| |
| static const ARMCPRegInfo cp_reginfo[] = { |
| /* Define the secure and non-secure FCSE identifier CP registers |
| * separately because there is no secure bank in V8 (no _EL3). This allows |
| * the secure register to be properly reset and migrated. There is also no |
| * v8 EL1 version of the register so the non-secure instance stands alone. |
| */ |
| { .name = "FCSEIDR", |
| .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, |
| .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, |
| .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_ns), |
| .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, |
| { .name = "FCSEIDR_S", |
| .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, |
| .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, |
| .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_s), |
| .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, |
| /* Define the secure and non-secure context identifier CP registers |
| * separately because there is no secure bank in V8 (no _EL3). This allows |
| * the secure register to be properly reset and migrated. In the |
| * non-secure case, the 32-bit register will have reset and migration |
| * disabled during registration as it is handled by the 64-bit instance. |
| */ |
| { .name = "CONTEXTIDR_EL1", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .secure = ARM_CP_SECSTATE_NS, |
| .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), |
| .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, |
| { .name = "CONTEXTIDR_S", .state = ARM_CP_STATE_AA32, |
| .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .secure = ARM_CP_SECSTATE_S, |
| .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), |
| .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo not_v8_cp_reginfo[] = { |
| /* NB: Some of these registers exist in v8 but with more precise |
| * definitions that don't use CP_ANY wildcards (mostly in v8_cp_reginfo[]). |
| */ |
| /* MMU Domain access control / MPU write buffer control */ |
| { .name = "DACR", |
| .cp = 15, .opc1 = CP_ANY, .crn = 3, .crm = CP_ANY, .opc2 = CP_ANY, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, .resetvalue = 0, |
| .writefn = dacr_write, .raw_writefn = raw_write, |
| .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), |
| offsetoflow32(CPUARMState, cp15.dacr_ns) } }, |
| /* ARMv7 allocates a range of implementation defined TLB LOCKDOWN regs. |
| * For v6 and v5, these mappings are overly broad. |
| */ |
| { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 0, |
| .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, |
| { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 1, |
| .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, |
| { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 4, |
| .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, |
| { .name = "TLB_LOCKDOWN", .cp = 15, .crn = 10, .crm = 8, |
| .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .type = ARM_CP_NOP }, |
| /* 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 |
| }; |
| |
| static const ARMCPRegInfo not_v6_cp_reginfo[] = { |
| /* Not all pre-v6 cores implemented this WFI, so this is slightly |
| * over-broad. |
| */ |
| { .name = "WFI_v5", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = 2, |
| .access = PL1_W, .type = ARM_CP_WFI }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo not_v7_cp_reginfo[] = { |
| /* Standard v6 WFI (also used in some pre-v6 cores); not in v7 (which |
| * is UNPREDICTABLE; we choose to NOP as most implementations do). |
| */ |
| { .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 | ARM_CP_NO_RAW, |
| .resetvalue = 0 }, |
| /* We don't implement pre-v7 debug but most CPUs had at least a DBGDIDR; |
| * implementing it as RAZ means the "debug architecture version" bits |
| * will read as a reserved value, which should cause Linux to not try |
| * to use the debug hardware. |
| */ |
| { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, |
| .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, |
| /* 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, |
| .type = ARM_CP_NO_RAW }, |
| { .name = "TLBIMVA", .cp = 15, .crn = 8, .crm = CP_ANY, |
| .opc1 = CP_ANY, .opc2 = 1, .access = PL1_W, .writefn = tlbimva_write, |
| .type = ARM_CP_NO_RAW }, |
| { .name = "TLBIASID", .cp = 15, .crn = 8, .crm = CP_ANY, |
| .opc1 = CP_ANY, .opc2 = 2, .access = PL1_W, .writefn = tlbiasid_write, |
| .type = ARM_CP_NO_RAW }, |
| { .name = "TLBIMVAA", .cp = 15, .crn = 8, .crm = CP_ANY, |
| .opc1 = CP_ANY, .opc2 = 3, .access = PL1_W, .writefn = tlbimvaa_write, |
| .type = ARM_CP_NO_RAW }, |
| { .name = "PRRR", .cp = 15, .crn = 10, .crm = 2, |
| .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_NOP }, |
| { .name = "NMRR", .cp = 15, .crn = 10, .crm = 2, |
| .opc1 = 0, .opc2 = 1, .access = PL1_RW, .type = ARM_CP_NOP }, |
| REGINFO_SENTINEL |
| }; |
| |
| static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint32_t mask = 0; |
| |
| /* In ARMv8 most bits of CPACR_EL1 are RES0. */ |
| if (!arm_feature(env, ARM_FEATURE_V8)) { |
| /* ARMv7 defines bits for unimplemented coprocessors as RAZ/WI. |
| * ASEDIS [31] and D32DIS [30] are both UNK/SBZP without VFP. |
| * TRCDIS [28] is RAZ/WI since we do not implement a trace macrocell. |
| */ |
| if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { |
| /* VFP coprocessor: cp10 & cp11 [23:20] */ |
| mask |= (1 << 31) | (1 << 30) | (0xf << 20); |
| |
| if (!arm_feature(env, ARM_FEATURE_NEON)) { |
| /* ASEDIS [31] bit is RAO/WI */ |
| value |= (1 << 31); |
| } |
| |
| /* VFPv3 and upwards with NEON implement 32 double precision |
| * registers (D0-D31). |
| */ |
| if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) { |
| /* D32DIS [30] is RAO/WI if D16-31 are not implemented. */ |
| value |= (1 << 30); |
| } |
| } |
| value &= mask; |
| } |
| |
| /* |
| * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 |
| * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. |
| */ |
| if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && |
| !arm_is_secure(env) && !extract32(env->cp15.nsacr, 10, 1)) { |
| value &= ~(0xf << 20); |
| value |= env->cp15.cpacr_el1 & (0xf << 20); |
| } |
| |
| env->cp15.cpacr_el1 = value; |
| } |
| |
| static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| /* |
| * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 |
| * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. |
| */ |
| uint64_t value = env->cp15.cpacr_el1; |
| |
| if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && |
| !arm_is_secure(env) && !extract32(env->cp15.nsacr, 10, 1)) { |
| value &= ~(0xf << 20); |
| } |
| return value; |
| } |
| |
| |
| static void cpacr_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| /* Call cpacr_write() so that we reset with the correct RAO bits set |
| * for our CPU features. |
| */ |
| cpacr_write(env, ri, 0); |
| } |
| |
| static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_feature(env, ARM_FEATURE_V8)) { |
| /* Check if CPACR accesses are to be trapped to EL2 */ |
| if (arm_current_el(env) == 1 && |
| (env->cp15.cptr_el[2] & CPTR_TCPAC) && !arm_is_secure(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| /* Check if CPACR accesses are to be trapped to EL3 */ |
| } else if (arm_current_el(env) < 3 && |
| (env->cp15.cptr_el[3] & CPTR_TCPAC)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult cptr_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* Check if CPTR accesses are set to trap to EL3 */ |
| if (arm_current_el(env) == 2 && (env->cp15.cptr_el[3] & CPTR_TCPAC)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static const ARMCPRegInfo v6_cp_reginfo[] = { |
| /* prefetch by MVA in v6, NOP in v7 */ |
| { .name = "MVA_prefetch", |
| .cp = 15, .crn = 7, .crm = 13, .opc1 = 0, .opc2 = 1, |
| .access = PL1_W, .type = ARM_CP_NOP }, |
| /* We need to break the TB after ISB to execute self-modifying code |
| * correctly and also to take any pending interrupts immediately. |
| * So use arm_cp_write_ignore() function instead of ARM_CP_NOP flag. |
| */ |
| { .name = "ISB", .cp = 15, .crn = 7, .crm = 5, .opc1 = 0, .opc2 = 4, |
| .access = PL0_W, .type = ARM_CP_NO_RAW, .writefn = arm_cp_write_ignore }, |
| { .name = "DSB", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 4, |
| .access = PL0_W, .type = ARM_CP_NOP }, |
| { .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, .accessfn = access_tvm_trvm, |
| .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ifar_s), |
| offsetof(CPUARMState, cp15.ifar_ns) }, |
| .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", .state = ARM_CP_STATE_BOTH, .opc0 = 3, |
| .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, |
| .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), |
| .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, |
| REGINFO_SENTINEL |
| }; |
| |
| /* Definitions for the PMU registers */ |
| #define PMCRN_MASK 0xf800 |
| #define PMCRN_SHIFT 11 |
| #define PMCRLC 0x40 |
| #define PMCRDP 0x20 |
| #define PMCRX 0x10 |
| #define PMCRD 0x8 |
| #define PMCRC 0x4 |
| #define PMCRP 0x2 |
| #define PMCRE 0x1 |
| /* |
| * Mask of PMCR bits writeable by guest (not including WO bits like C, P, |
| * which can be written as 1 to trigger behaviour but which stay RAZ). |
| */ |
| #define PMCR_WRITEABLE_MASK (PMCRLC | PMCRDP | PMCRX | PMCRD | PMCRE) |
| |
| #define PMXEVTYPER_P 0x80000000 |
| #define PMXEVTYPER_U 0x40000000 |
| #define PMXEVTYPER_NSK 0x20000000 |
| #define PMXEVTYPER_NSU 0x10000000 |
| #define PMXEVTYPER_NSH 0x08000000 |
| #define PMXEVTYPER_M 0x04000000 |
| #define PMXEVTYPER_MT 0x02000000 |
| #define PMXEVTYPER_EVTCOUNT 0x0000ffff |
| #define PMXEVTYPER_MASK (PMXEVTYPER_P | PMXEVTYPER_U | PMXEVTYPER_NSK | \ |
| PMXEVTYPER_NSU | PMXEVTYPER_NSH | \ |
| PMXEVTYPER_M | PMXEVTYPER_MT | \ |
| PMXEVTYPER_EVTCOUNT) |
| |
| #define PMCCFILTR 0xf8000000 |
| #define PMCCFILTR_M PMXEVTYPER_M |
| #define PMCCFILTR_EL0 (PMCCFILTR | PMCCFILTR_M) |
| |
| static inline uint32_t pmu_num_counters(CPUARMState *env) |
| { |
| return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT; |
| } |
| |
| /* Bits allowed to be set/cleared for PMCNTEN* and PMINTEN* */ |
| static inline uint64_t pmu_counter_mask(CPUARMState *env) |
| { |
| return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); |
| } |
| |
| typedef struct pm_event { |
| uint16_t number; /* PMEVTYPER.evtCount is 16 bits wide */ |
| /* If the event is supported on this CPU (used to generate PMCEID[01]) */ |
| bool (*supported)(CPUARMState *); |
| /* |
| * Retrieve the current count of the underlying event. The programmed |
| * counters hold a difference from the return value from this function |
| */ |
| uint64_t (*get_count)(CPUARMState *); |
| /* |
| * Return how many nanoseconds it will take (at a minimum) for count events |
| * to occur. A negative value indicates the counter will never overflow, or |
| * that the counter has otherwise arranged for the overflow bit to be set |
| * and the PMU interrupt to be raised on overflow. |
| */ |
| int64_t (*ns_per_count)(uint64_t); |
| } pm_event; |
| |
| static bool event_always_supported(CPUARMState *env) |
| { |
| return true; |
| } |
| |
| static uint64_t swinc_get_count(CPUARMState *env) |
| { |
| /* |
| * SW_INCR events are written directly to the pmevcntr's by writes to |
| * PMSWINC, so there is no underlying count maintained by the PMU itself |
| */ |
| return 0; |
| } |
| |
| static int64_t swinc_ns_per(uint64_t ignored) |
| { |
| return -1; |
| } |
| |
| /* |
| * Return the underlying cycle count for the PMU cycle counters. If we're in |
| * usermode, simply return 0. |
| */ |
| static uint64_t cycles_get_count(CPUARMState *env) |
| { |
| #ifndef CONFIG_USER_ONLY |
| return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), |
| ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); |
| #else |
| return cpu_get_host_ticks(); |
| #endif |
| } |
| |
| #ifndef CONFIG_USER_ONLY |
| static int64_t cycles_ns_per(uint64_t cycles) |
| { |
| return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles; |
| } |
| |
| static bool instructions_supported(CPUARMState *env) |
| { |
| return use_icount == 1 /* Precise instruction counting */; |
| } |
| |
| static uint64_t instructions_get_count(CPUARMState *env) |
| { |
| return (uint64_t)cpu_get_icount_raw(); |
| } |
| |
| static int64_t instructions_ns_per(uint64_t icount) |
| { |
| return cpu_icount_to_ns((int64_t)icount); |
| } |
| #endif |
| |
| static bool pmu_8_1_events_supported(CPUARMState *env) |
| { |
| /* For events which are supported in any v8.1 PMU */ |
| return cpu_isar_feature(any_pmu_8_1, env_archcpu(env)); |
| } |
| |
| static bool pmu_8_4_events_supported(CPUARMState *env) |
| { |
| /* For events which are supported in any v8.1 PMU */ |
| return cpu_isar_feature(any_pmu_8_4, env_archcpu(env)); |
| } |
| |
| static uint64_t zero_event_get_count(CPUARMState *env) |
| { |
| /* For events which on QEMU never fire, so their count is always zero */ |
| return 0; |
| } |
| |
| static int64_t zero_event_ns_per(uint64_t cycles) |
| { |
| /* An event which never fires can never overflow */ |
| return -1; |
| } |
| |
| static const pm_event pm_events[] = { |
| { .number = 0x000, /* SW_INCR */ |
| .supported = event_always_supported, |
| .get_count = swinc_get_count, |
| .ns_per_count = swinc_ns_per, |
| }, |
| #ifndef CONFIG_USER_ONLY |
| { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */ |
| .supported = instructions_supported, |
| .get_count = instructions_get_count, |
| .ns_per_count = instructions_ns_per, |
| }, |
| { .number = 0x011, /* CPU_CYCLES, Cycle */ |
| .supported = event_always_supported, |
| .get_count = cycles_get_count, |
| .ns_per_count = cycles_ns_per, |
| }, |
| #endif |
| { .number = 0x023, /* STALL_FRONTEND */ |
| .supported = pmu_8_1_events_supported, |
| .get_count = zero_event_get_count, |
| .ns_per_count = zero_event_ns_per, |
| }, |
| { .number = 0x024, /* STALL_BACKEND */ |
| .supported = pmu_8_1_events_supported, |
| .get_count = zero_event_get_count, |
| .ns_per_count = zero_event_ns_per, |
| }, |
| { .number = 0x03c, /* STALL */ |
| .supported = pmu_8_4_events_supported, |
| .get_count = zero_event_get_count, |
| .ns_per_count = zero_event_ns_per, |
| }, |
| }; |
| |
| /* |
| * Note: Before increasing MAX_EVENT_ID beyond 0x3f into the 0x40xx range of |
| * events (i.e. the statistical profiling extension), this implementation |
| * should first be updated to something sparse instead of the current |
| * supported_event_map[] array. |
| */ |
| #define MAX_EVENT_ID 0x3c |
| #define UNSUPPORTED_EVENT UINT16_MAX |
| static uint16_t supported_event_map[MAX_EVENT_ID + 1]; |
| |
| /* |
| * Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map |
| * of ARM event numbers to indices in our pm_events array. |
| * |
| * Note: Events in the 0x40XX range are not currently supported. |
| */ |
| void pmu_init(ARMCPU *cpu) |
| { |
| unsigned int i; |
| |
| /* |
| * Empty supported_event_map and cpu->pmceid[01] before adding supported |
| * events to them |
| */ |
| for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) { |
| supported_event_map[i] = UNSUPPORTED_EVENT; |
| } |
| cpu->pmceid0 = 0; |
| cpu->pmceid1 = 0; |
| |
| for (i = 0; i < ARRAY_SIZE(pm_events); i++) { |
| const pm_event *cnt = &pm_events[i]; |
| assert(cnt->number <= MAX_EVENT_ID); |
| /* We do not currently support events in the 0x40xx range */ |
| assert(cnt->number <= 0x3f); |
| |
| if (cnt->supported(&cpu->env)) { |
| supported_event_map[cnt->number] = i; |
| uint64_t event_mask = 1ULL << (cnt->number & 0x1f); |
| if (cnt->number & 0x20) { |
| cpu->pmceid1 |= event_mask; |
| } else { |
| cpu->pmceid0 |= event_mask; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Check at runtime whether a PMU event is supported for the current machine |
| */ |
| static bool event_supported(uint16_t number) |
| { |
| if (number > MAX_EVENT_ID) { |
| return false; |
| } |
| return supported_event_map[number] != UNSUPPORTED_EVENT; |
| } |
| |
| static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* Performance monitor registers user accessibility is controlled |
| * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable |
| * trapping to EL2 or EL3 for other accesses. |
| */ |
| int el = arm_current_el(env); |
| |
| if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { |
| return CP_ACCESS_TRAP; |
| } |
| if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TPM) |
| && !arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* ER: event counter read trap control */ |
| if (arm_feature(env, ARM_FEATURE_V8) |
| && arm_current_el(env) == 0 |
| && (env->cp15.c9_pmuserenr & (1 << 3)) != 0 |
| && isread) { |
| return CP_ACCESS_OK; |
| } |
| |
| return pmreg_access(env, ri, isread); |
| } |
| |
| static CPAccessResult pmreg_access_swinc(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* SW: software increment write trap control */ |
| if (arm_feature(env, ARM_FEATURE_V8) |
| && arm_current_el(env) == 0 |
| && (env->cp15.c9_pmuserenr & (1 << 1)) != 0 |
| && !isread) { |
| return CP_ACCESS_OK; |
| } |
| |
| return pmreg_access(env, ri, isread); |
| } |
| |
| static CPAccessResult pmreg_access_selr(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* ER: event counter read trap control */ |
| if (arm_feature(env, ARM_FEATURE_V8) |
| && arm_current_el(env) == 0 |
| && (env->cp15.c9_pmuserenr & (1 << 3)) != 0) { |
| return CP_ACCESS_OK; |
| } |
| |
| return pmreg_access(env, ri, isread); |
| } |
| |
| static CPAccessResult pmreg_access_ccntr(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* CR: cycle counter read trap control */ |
| if (arm_feature(env, ARM_FEATURE_V8) |
| && arm_current_el(env) == 0 |
| && (env->cp15.c9_pmuserenr & (1 << 2)) != 0 |
| && isread) { |
| return CP_ACCESS_OK; |
| } |
| |
| return pmreg_access(env, ri, isread); |
| } |
| |
| /* Returns true if the counter (pass 31 for PMCCNTR) should count events using |
| * the current EL, security state, and register configuration. |
| */ |
| static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) |
| { |
| uint64_t filter; |
| bool e, p, u, nsk, nsu, nsh, m; |
| bool enabled, prohibited, filtered; |
| bool secure = arm_is_secure(env); |
| int el = arm_current_el(env); |
| uint8_t hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; |
| |
| if (!arm_feature(env, ARM_FEATURE_PMU)) { |
| return false; |
| } |
| |
| if (!arm_feature(env, ARM_FEATURE_EL2) || |
| (counter < hpmn || counter == 31)) { |
| e = env->cp15.c9_pmcr & PMCRE; |
| } else { |
| e = env->cp15.mdcr_el2 & MDCR_HPME; |
| } |
| enabled = e && (env->cp15.c9_pmcnten & (1 << counter)); |
| |
| if (!secure) { |
| if (el == 2 && (counter < hpmn || counter == 31)) { |
| prohibited = env->cp15.mdcr_el2 & MDCR_HPMD; |
| } else { |
| prohibited = false; |
| } |
| } else { |
| prohibited = arm_feature(env, ARM_FEATURE_EL3) && |
| (env->cp15.mdcr_el3 & MDCR_SPME); |
| } |
| |
| if (prohibited && counter == 31) { |
| prohibited = env->cp15.c9_pmcr & PMCRDP; |
| } |
| |
| if (counter == 31) { |
| filter = env->cp15.pmccfiltr_el0; |
| } else { |
| filter = env->cp15.c14_pmevtyper[counter]; |
| } |
| |
| p = filter & PMXEVTYPER_P; |
| u = filter & PMXEVTYPER_U; |
| nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK); |
| nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU); |
| nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH); |
| m = arm_el_is_aa64(env, 1) && |
| arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M); |
| |
| if (el == 0) { |
| filtered = secure ? u : u != nsu; |
| } else if (el == 1) { |
| filtered = secure ? p : p != nsk; |
| } else if (el == 2) { |
| filtered = !nsh; |
| } else { /* EL3 */ |
| filtered = m != p; |
| } |
| |
| if (counter != 31) { |
| /* |
| * If not checking PMCCNTR, ensure the counter is setup to an event we |
| * support |
| */ |
| uint16_t event = filter & PMXEVTYPER_EVTCOUNT; |
| if (!event_supported(event)) { |
| return false; |
| } |
| } |
| |
| return enabled && !prohibited && !filtered; |
| } |
| |
| static void pmu_update_irq(CPUARMState *env) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) && |
| (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); |
| } |
| |
| /* |
| * Ensure c15_ccnt is the guest-visible count so that operations such as |
| * enabling/disabling the counter or filtering, modifying the count itself, |
| * etc. can be done logically. This is essentially a no-op if the counter is |
| * not enabled at the time of the call. |
| */ |
| static void pmccntr_op_start(CPUARMState *env) |
| { |
| uint64_t cycles = cycles_get_count(env); |
| |
| if (pmu_counter_enabled(env, 31)) { |
| uint64_t eff_cycles = cycles; |
| if (env->cp15.c9_pmcr & PMCRD) { |
| /* Increment once every 64 processor clock cycles */ |
| eff_cycles /= 64; |
| } |
| |
| uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta; |
| |
| uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ |
| 1ull << 63 : 1ull << 31; |
| if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { |
| env->cp15.c9_pmovsr |= (1 << 31); |
| pmu_update_irq(env); |
| } |
| |
| env->cp15.c15_ccnt = new_pmccntr; |
| } |
| env->cp15.c15_ccnt_delta = cycles; |
| } |
| |
| /* |
| * If PMCCNTR is enabled, recalculate the delta between the clock and the |
| * guest-visible count. A call to pmccntr_op_finish should follow every call to |
| * pmccntr_op_start. |
| */ |
| static void pmccntr_op_finish(CPUARMState *env) |
| { |
| if (pmu_counter_enabled(env, 31)) { |
| #ifndef CONFIG_USER_ONLY |
| /* Calculate when the counter will next overflow */ |
| uint64_t remaining_cycles = -env->cp15.c15_ccnt; |
| if (!(env->cp15.c9_pmcr & PMCRLC)) { |
| remaining_cycles = (uint32_t)remaining_cycles; |
| } |
| int64_t overflow_in = cycles_ns_per(remaining_cycles); |
| |
| if (overflow_in > 0) { |
| int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + |
| overflow_in; |
| ARMCPU *cpu = env_archcpu(env); |
| timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); |
| } |
| #endif |
| |
| uint64_t prev_cycles = env->cp15.c15_ccnt_delta; |
| if (env->cp15.c9_pmcr & PMCRD) { |
| /* Increment once every 64 processor clock cycles */ |
| prev_cycles /= 64; |
| } |
| env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt; |
| } |
| } |
| |
| static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) |
| { |
| |
| uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; |
| uint64_t count = 0; |
| if (event_supported(event)) { |
| uint16_t event_idx = supported_event_map[event]; |
| count = pm_events[event_idx].get_count(env); |
| } |
| |
| if (pmu_counter_enabled(env, counter)) { |
| uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; |
| |
| if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) { |
| env->cp15.c9_pmovsr |= (1 << counter); |
| pmu_update_irq(env); |
| } |
| env->cp15.c14_pmevcntr[counter] = new_pmevcntr; |
| } |
| env->cp15.c14_pmevcntr_delta[counter] = count; |
| } |
| |
| static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter) |
| { |
| if (pmu_counter_enabled(env, counter)) { |
| #ifndef CONFIG_USER_ONLY |
| uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; |
| uint16_t event_idx = supported_event_map[event]; |
| uint64_t delta = UINT32_MAX - |
| (uint32_t)env->cp15.c14_pmevcntr[counter] + 1; |
| int64_t overflow_in = pm_events[event_idx].ns_per_count(delta); |
| |
| if (overflow_in > 0) { |
| int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + |
| overflow_in; |
| ARMCPU *cpu = env_archcpu(env); |
| timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); |
| } |
| #endif |
| |
| env->cp15.c14_pmevcntr_delta[counter] -= |
| env->cp15.c14_pmevcntr[counter]; |
| } |
| } |
| |
| void pmu_op_start(CPUARMState *env) |
| { |
| unsigned int i; |
| pmccntr_op_start(env); |
| for (i = 0; i < pmu_num_counters(env); i++) { |
| pmevcntr_op_start(env, i); |
| } |
| } |
| |
| void pmu_op_finish(CPUARMState *env) |
| { |
| unsigned int i; |
| pmccntr_op_finish(env); |
| for (i = 0; i < pmu_num_counters(env); i++) { |
| pmevcntr_op_finish(env, i); |
| } |
| } |
| |
| void pmu_pre_el_change(ARMCPU *cpu, void *ignored) |
| { |
| pmu_op_start(&cpu->env); |
| } |
| |
| void pmu_post_el_change(ARMCPU *cpu, void *ignored) |
| { |
| pmu_op_finish(&cpu->env); |
| } |
| |
| void arm_pmu_timer_cb(void *opaque) |
| { |
| ARMCPU *cpu = opaque; |
| |
| /* |
| * Update all the counter values based on the current underlying counts, |
| * triggering interrupts to be raised, if necessary. pmu_op_finish() also |
| * has the effect of setting the cpu->pmu_timer to the next earliest time a |
| * counter may expire. |
| */ |
| pmu_op_start(&cpu->env); |
| pmu_op_finish(&cpu->env); |
| } |
| |
| static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmu_op_start(env); |
| |
| if (value & PMCRC) { |
| /* The counter has been reset */ |
| env->cp15.c15_ccnt = 0; |
| } |
| |
| if (value & PMCRP) { |
| unsigned int i; |
| for (i = 0; i < pmu_num_counters(env); i++) { |
| env->cp15.c14_pmevcntr[i] = 0; |
| } |
| } |
| |
| env->cp15.c9_pmcr &= ~PMCR_WRITEABLE_MASK; |
| env->cp15.c9_pmcr |= (value & PMCR_WRITEABLE_MASK); |
| |
| pmu_op_finish(env); |
| } |
| |
| static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| unsigned int i; |
| for (i = 0; i < pmu_num_counters(env); i++) { |
| /* Increment a counter's count iff: */ |
| if ((value & (1 << i)) && /* counter's bit is set */ |
| /* counter is enabled and not filtered */ |
| pmu_counter_enabled(env, i) && |
| /* counter is SW_INCR */ |
| (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) { |
| pmevcntr_op_start(env, i); |
| |
| /* |
| * Detect if this write causes an overflow since we can't predict |
| * PMSWINC overflows like we can for other events |
| */ |
| uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; |
| |
| if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) { |
| env->cp15.c9_pmovsr |= (1 << i); |
| pmu_update_irq(env); |
| } |
| |
| env->cp15.c14_pmevcntr[i] = new_pmswinc; |
| |
| pmevcntr_op_finish(env, i); |
| } |
| } |
| } |
| |
| static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint64_t ret; |
| pmccntr_op_start(env); |
| ret = env->cp15.c15_ccnt; |
| pmccntr_op_finish(env); |
| return ret; |
| } |
| |
| static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* The value of PMSELR.SEL affects the behavior of PMXEVTYPER and |
| * PMXEVCNTR. We allow [0..31] to be written to PMSELR here; in the |
| * meanwhile, we check PMSELR.SEL when PMXEVTYPER and PMXEVCNTR are |
| * accessed. |
| */ |
| env->cp15.c9_pmselr = value & 0x1f; |
| } |
| |
| static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmccntr_op_start(env); |
| env->cp15.c15_ccnt = value; |
| pmccntr_op_finish(env); |
| } |
| |
| static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint64_t cur_val = pmccntr_read(env, NULL); |
| |
| pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value)); |
| } |
| |
| static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmccntr_op_start(env); |
| env->cp15.pmccfiltr_el0 = value & PMCCFILTR_EL0; |
| pmccntr_op_finish(env); |
| } |
| |
| static void pmccfiltr_write_a32(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmccntr_op_start(env); |
| /* M is not accessible from AArch32 */ |
| env->cp15.pmccfiltr_el0 = (env->cp15.pmccfiltr_el0 & PMCCFILTR_M) | |
| (value & PMCCFILTR); |
| pmccntr_op_finish(env); |
| } |
| |
| static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| /* M is not visible in AArch32 */ |
| return env->cp15.pmccfiltr_el0 & PMCCFILTR; |
| } |
| |
| static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pmcnten |= value; |
| } |
| |
| static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pmcnten &= ~value; |
| } |
| |
| static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pmovsr &= ~value; |
| pmu_update_irq(env); |
| } |
| |
| static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pmovsr |= value; |
| pmu_update_irq(env); |
| } |
| |
| static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value, const uint8_t counter) |
| { |
| if (counter == 31) { |
| pmccfiltr_write(env, ri, value); |
| } else if (counter < pmu_num_counters(env)) { |
| pmevcntr_op_start(env, counter); |
| |
| /* |
| * If this counter's event type is changing, store the current |
| * underlying count for the new type in c14_pmevcntr_delta[counter] so |
| * pmevcntr_op_finish has the correct baseline when it converts back to |
| * a delta. |
| */ |
| uint16_t old_event = env->cp15.c14_pmevtyper[counter] & |
| PMXEVTYPER_EVTCOUNT; |
| uint16_t new_event = value & PMXEVTYPER_EVTCOUNT; |
| if (old_event != new_event) { |
| uint64_t count = 0; |
| if (event_supported(new_event)) { |
| uint16_t event_idx = supported_event_map[new_event]; |
| count = pm_events[event_idx].get_count(env); |
| } |
| env->cp15.c14_pmevcntr_delta[counter] = count; |
| } |
| |
| env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK; |
| pmevcntr_op_finish(env, counter); |
| } |
| /* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when |
| * PMSELR value is equal to or greater than the number of implemented |
| * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. |
| */ |
| } |
| |
| static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri, |
| const uint8_t counter) |
| { |
| if (counter == 31) { |
| return env->cp15.pmccfiltr_el0; |
| } else if (counter < pmu_num_counters(env)) { |
| return env->cp15.c14_pmevtyper[counter]; |
| } else { |
| /* |
| * We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER |
| * are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write(). |
| */ |
| return 0; |
| } |
| } |
| |
| static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| pmevtyper_write(env, ri, value, counter); |
| } |
| |
| static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| env->cp15.c14_pmevtyper[counter] = value; |
| |
| /* |
| * pmevtyper_rawwrite is called between a pair of pmu_op_start and |
| * pmu_op_finish calls when loading saved state for a migration. Because |
| * we're potentially updating the type of event here, the value written to |
| * c14_pmevcntr_delta by the preceeding pmu_op_start call may be for a |
| * different counter type. Therefore, we need to set this value to the |
| * current count for the counter type we're writing so that pmu_op_finish |
| * has the correct count for its calculation. |
| */ |
| uint16_t event = value & PMXEVTYPER_EVTCOUNT; |
| if (event_supported(event)) { |
| uint16_t event_idx = supported_event_map[event]; |
| env->cp15.c14_pmevcntr_delta[counter] = |
| pm_events[event_idx].get_count(env); |
| } |
| } |
| |
| static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| return pmevtyper_read(env, ri, counter); |
| } |
| |
| static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31); |
| } |
| |
| static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31); |
| } |
| |
| static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value, uint8_t counter) |
| { |
| if (counter < pmu_num_counters(env)) { |
| pmevcntr_op_start(env, counter); |
| env->cp15.c14_pmevcntr[counter] = value; |
| pmevcntr_op_finish(env, counter); |
| } |
| /* |
| * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR |
| * are CONSTRAINED UNPREDICTABLE. |
| */ |
| } |
| |
| static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint8_t counter) |
| { |
| if (counter < pmu_num_counters(env)) { |
| uint64_t ret; |
| pmevcntr_op_start(env, counter); |
| ret = env->cp15.c14_pmevcntr[counter]; |
| pmevcntr_op_finish(env, counter); |
| return ret; |
| } else { |
| /* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR |
| * are CONSTRAINED UNPREDICTABLE. */ |
| return 0; |
| } |
| } |
| |
| static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| pmevcntr_write(env, ri, value, counter); |
| } |
| |
| static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| return pmevcntr_read(env, ri, counter); |
| } |
| |
| static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| assert(counter < pmu_num_counters(env)); |
| env->cp15.c14_pmevcntr[counter] = value; |
| pmevcntr_write(env, ri, value, counter); |
| } |
| |
| static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); |
| assert(counter < pmu_num_counters(env)); |
| return env->cp15.c14_pmevcntr[counter]; |
| } |
| |
| static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31); |
| } |
| |
| static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31); |
| } |
| |
| static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| if (arm_feature(env, ARM_FEATURE_V8)) { |
| env->cp15.c9_pmuserenr = value & 0xf; |
| } else { |
| env->cp15.c9_pmuserenr = value & 1; |
| } |
| } |
| |
| static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* We have no event counters so only the C bit can be changed */ |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pminten |= value; |
| pmu_update_irq(env); |
| } |
| |
| static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= pmu_counter_mask(env); |
| env->cp15.c9_pminten &= ~value; |
| pmu_update_irq(env); |
| } |
| |
| static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Note that even though the AArch64 view of this register has bits |
| * [10:0] all RES0 we can only mask the bottom 5, to comply with the |
| * architectural requirements for bits which are RES0 only in some |
| * contexts. (ARMv8 would permit us to do no masking at all, but ARMv7 |
| * requires the bottom five bits to be RAZ/WI because they're UNK/SBZP.) |
| */ |
| raw_write(env, ri, value & ~0x1FULL); |
| } |
| |
| static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| /* Begin with base v8.0 state. */ |
| uint32_t valid_mask = 0x3fff; |
| ARMCPU *cpu = env_archcpu(env); |
| |
| if (arm_el_is_aa64(env, 3)) { |
| value |= SCR_FW | SCR_AW; /* these two bits are RES1. */ |
| valid_mask &= ~SCR_NET; |
| } else { |
| valid_mask &= ~(SCR_RW | SCR_ST); |
| } |
| |
| if (!arm_feature(env, ARM_FEATURE_EL2)) { |
| valid_mask &= ~SCR_HCE; |
| |
| /* On ARMv7, SMD (or SCD as it is called in v7) is only |
| * supported if EL2 exists. The bit is UNK/SBZP when |
| * EL2 is unavailable. In QEMU ARMv7, we force it to always zero |
| * when EL2 is unavailable. |
| * On ARMv8, this bit is always available. |
| */ |
| if (arm_feature(env, ARM_FEATURE_V7) && |
| !arm_feature(env, ARM_FEATURE_V8)) { |
| valid_mask &= ~SCR_SMD; |
| } |
| } |
| if (cpu_isar_feature(aa64_lor, cpu)) { |
| valid_mask |= SCR_TLOR; |
| } |
| if (cpu_isar_feature(aa64_pauth, cpu)) { |
| valid_mask |= SCR_API | SCR_APK; |
| } |
| |
| /* Clear all-context RES0 bits. */ |
| value &= valid_mask; |
| raw_write(env, ri, value); |
| } |
| |
| static CPAccessResult access_aa64_tid2(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID2)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| ARMCPU *cpu = env_archcpu(env); |
| |
| /* Acquire the CSSELR index from the bank corresponding to the CCSIDR |
| * bank |
| */ |
| uint32_t index = A32_BANKED_REG_GET(env, csselr, |
| ri->secure & ARM_CP_SECSTATE_S); |
| |
| return cpu->ccsidr[index]; |
| } |
| |
| static void csselr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| raw_write(env, ri, value & 0xf); |
| } |
| |
| static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| CPUState *cs = env_cpu(env); |
| uint64_t hcr_el2 = arm_hcr_el2_eff(env); |
| uint64_t ret = 0; |
| bool allow_virt = (arm_current_el(env) == 1 && |
| (!arm_is_secure_below_el3(env) || |
| (env->cp15.scr_el3 & SCR_EEL2))); |
| |
| if (allow_virt && (hcr_el2 & HCR_IMO)) { |
| if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { |
| ret |= CPSR_I; |
| } |
| } else { |
| if (cs->interrupt_request & CPU_INTERRUPT_HARD) { |
| ret |= CPSR_I; |
| } |
| } |
| |
| if (allow_virt && (hcr_el2 & HCR_FMO)) { |
| if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { |
| ret |= CPSR_F; |
| } |
| } else { |
| if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { |
| ret |= CPSR_F; |
| } |
| } |
| |
| /* External aborts are not possible in QEMU so A bit is always clear */ |
| return ret; |
| } |
| |
| static CPAccessResult access_aa64_tid1(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_TID1)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult access_aa32_tid1(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_feature(env, ARM_FEATURE_V8)) { |
| return access_aa64_tid1(env, ri, isread); |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static const ARMCPRegInfo v7_cp_reginfo[] = { |
| /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */ |
| { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, |
| .access = PL1_W, .type = ARM_CP_NOP }, |
| /* Performance monitors are implementation defined in v7, |
| * but with an ARM recommended set of registers, which we |
| * follow. |
| * |
| * Performance registers fall into three categories: |
| * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR) |
| * (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR) |
| * (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others) |
| * For the cases controlled by PMUSERENR we must set .access to PL0_RW |
| * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. |
| */ |
| { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, |
| .access = PL0_RW, .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), |
| .writefn = pmcntenset_write, |
| .accessfn = pmreg_access, |
| .raw_writefn = raw_write }, |
| { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, |
| .writefn = pmcntenset_write, .raw_writefn = raw_write }, |
| { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, |
| .access = PL0_RW, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), |
| .accessfn = pmreg_access, |
| .writefn = pmcntenclr_write, |
| .type = ARM_CP_ALIAS }, |
| { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), |
| .writefn = pmcntenclr_write }, |
| { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, |
| .access = PL0_RW, .type = ARM_CP_IO, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), |
| .accessfn = pmreg_access, |
| .writefn = pmovsr_write, |
| .raw_writefn = raw_write }, |
| { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), |
| .writefn = pmovsr_write, |
| .raw_writefn = raw_write }, |
| { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, |
| .access = PL0_W, .accessfn = pmreg_access_swinc, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .writefn = pmswinc_write }, |
| { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, |
| .access = PL0_W, .accessfn = pmreg_access_swinc, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .writefn = pmswinc_write }, |
| { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, |
| .access = PL0_RW, .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), |
| .accessfn = pmreg_access_selr, .writefn = pmselr_write, |
| .raw_writefn = raw_write}, |
| { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, |
| .access = PL0_RW, .accessfn = pmreg_access_selr, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), |
| .writefn = pmselr_write, .raw_writefn = raw_write, }, |
| { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, |
| .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .readfn = pmccntr_read, .writefn = pmccntr_write32, |
| .accessfn = pmreg_access_ccntr }, |
| { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, |
| .access = PL0_RW, .accessfn = pmreg_access_ccntr, |
| .type = ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), |
| .readfn = pmccntr_read, .writefn = pmccntr_write, |
| .raw_readfn = raw_read, .raw_writefn = raw_write, }, |
| { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, |
| .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .resetvalue = 0, }, |
| { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, |
| .writefn = pmccfiltr_write, .raw_writefn = raw_write, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), |
| .resetvalue = 0, }, |
| { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, |
| .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = pmreg_access, |
| .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, |
| { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, |
| .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = pmreg_access, |
| .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, |
| { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, |
| .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = pmreg_access_xevcntr, |
| .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, |
| { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, |
| .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = pmreg_access_xevcntr, |
| .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, |
| { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, |
| .access = PL0_R | PL1_RW, .accessfn = access_tpm, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), |
| .resetvalue = 0, |
| .writefn = pmuserenr_write, .raw_writefn = raw_write }, |
| { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0, |
| .access = PL0_R | PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), |
| .resetvalue = 0, |
| .writefn = pmuserenr_write, .raw_writefn = raw_write }, |
| { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tpm, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), |
| .resetvalue = 0, |
| .writefn = pmintenset_write, .raw_writefn = raw_write }, |
| { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tpm, |
| .type = ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), |
| .writefn = pmintenset_write, .raw_writefn = raw_write, |
| .resetvalue = 0x0 }, |
| { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, |
| .access = PL1_RW, .accessfn = access_tpm, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), |
| .writefn = pmintenclr_write, }, |
| { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, |
| .access = PL1_RW, .accessfn = access_tpm, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), |
| .writefn = pmintenclr_write }, |
| { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, |
| .access = PL1_R, |
| .accessfn = access_aa64_tid2, |
| .readfn = ccsidr_read, .type = ARM_CP_NO_RAW }, |
| { .name = "CSSELR", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 2, .opc2 = 0, |
| .access = PL1_RW, |
| .accessfn = access_aa64_tid2, |
| .writefn = csselr_write, .resetvalue = 0, |
| .bank_fieldoffsets = { offsetof(CPUARMState, cp15.csselr_s), |
| offsetof(CPUARMState, cp15.csselr_ns) } }, |
| /* Auxiliary ID register: this actually has an IMPDEF value but for now |
| * just RAZ for all cores: |
| */ |
| { .name = "AIDR", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 1, .crn = 0, .crm = 0, .opc2 = 7, |
| .access = PL1_R, .type = ARM_CP_CONST, |
| .accessfn = access_aa64_tid1, |
| .resetvalue = 0 }, |
| /* Auxiliary fault status registers: these also are IMPDEF, and we |
| * choose to RAZ/WI for all cores. |
| */ |
| { .name = "AFSR0_EL1", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 0, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .type = ARM_CP_CONST, .resetvalue = 0 }, |
| { .name = "AFSR1_EL1", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 0, .crn = 5, .crm = 1, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .type = ARM_CP_CONST, .resetvalue = 0 }, |
| /* MAIR can just read-as-written because we don't implement caches |
| * and so don't need to care about memory attributes. |
| */ |
| { .name = "MAIR_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .fieldoffset = offsetof(CPUARMState, cp15.mair_el[1]), |
| .resetvalue = 0 }, |
| { .name = "MAIR_EL3", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 6, .crn = 10, .crm = 2, .opc2 = 0, |
| .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[3]), |
| .resetvalue = 0 }, |
| /* For non-long-descriptor page tables these are PRRR and NMRR; |
| * regardless they still act as reads-as-written for QEMU. |
| */ |
| /* MAIR0/1 are defined separately from their 64-bit counterpart which |
| * allows them to assign the correct fieldoffset based on the endianness |
| * handled in the field definitions. |
| */ |
| { .name = "MAIR0", .state = ARM_CP_STATE_AA32, |
| .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 0, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair0_s), |
| offsetof(CPUARMState, cp15.mair0_ns) }, |
| .resetfn = arm_cp_reset_ignore }, |
| { .name = "MAIR1", .state = ARM_CP_STATE_AA32, |
| .cp = 15, .opc1 = 0, .crn = 10, .crm = 2, .opc2 = 1, |
| .access = PL1_RW, .accessfn = access_tvm_trvm, |
| .bank_fieldoffsets = { offsetof(CPUARMState, cp15.mair1_s), |
| offsetof(CPUARMState, cp15.mair1_ns) }, |
| .resetfn = arm_cp_reset_ignore }, |
| { .name = "ISR_EL1", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 1, .opc2 = 0, |
| .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, |
| /* 32 bit ITLB invalidates */ |
| { .name = "ITLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 0, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiall_write }, |
| { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimva_write }, |
| { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiasid_write }, |
| /* 32 bit DTLB invalidates */ |
| { .name = "DTLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 0, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiall_write }, |
| { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimva_write }, |
| { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiasid_write }, |
| /* 32 bit TLB invalidates */ |
| { .name = "TLBIALL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiall_write }, |
| { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimva_write }, |
| { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiasid_write }, |
| { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimvaa_write }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo v7mp_cp_reginfo[] = { |
| /* 32 bit TLB invalidates, Inner Shareable */ |
| { .name = "TLBIALLIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiall_is_write }, |
| { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimva_is_write }, |
| { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbiasid_is_write }, |
| { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .accessfn = access_ttlb, |
| .writefn = tlbimvaa_is_write }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo pmovsset_cp_reginfo[] = { |
| /* PMOVSSET is not implemented in v7 before v7ve */ |
| { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), |
| .writefn = pmovsset_write, |
| .raw_writefn = raw_write }, |
| { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_ALIAS | ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), |
| .writefn = pmovsset_write, |
| .raw_writefn = raw_write }, |
| REGINFO_SENTINEL |
| }; |
| |
| static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= 1; |
| env->teecr = value; |
| } |
| |
| static CPAccessResult teehbr_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 0 && (env->teecr & 1)) { |
| return CP_ACCESS_TRAP; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static const ARMCPRegInfo t2ee_cp_reginfo[] = { |
| { .name = "TEECR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 6, .opc2 = 0, |
| .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, teecr), |
| .resetvalue = 0, |
| .writefn = teecr_write }, |
| { .name = "TEEHBR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 6, .opc2 = 0, |
| .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, teehbr), |
| .accessfn = teehbr_access, .resetvalue = 0 }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo v6k_cp_reginfo[] = { |
| { .name = "TPIDR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .opc2 = 2, .crn = 13, .crm = 0, |
| .access = PL0_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[0]), .resetvalue = 0 }, |
| { .name = "TPIDRURW", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 2, |
| .access = PL0_RW, |
| .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrurw_s), |
| offsetoflow32(CPUARMState, cp15.tpidrurw_ns) }, |
| .resetfn = arm_cp_reset_ignore }, |
| { .name = "TPIDRRO_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .opc2 = 3, .crn = 13, .crm = 0, |
| .access = PL0_R|PL1_W, |
| .fieldoffset = offsetof(CPUARMState, cp15.tpidrro_el[0]), |
| .resetvalue = 0}, |
| { .name = "TPIDRURO", .cp = 15, .crn = 13, .crm = 0, .opc1 = 0, .opc2 = 3, |
| .access = PL0_R|PL1_W, |
| .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidruro_s), |
| offsetoflow32(CPUARMState, cp15.tpidruro_ns) }, |
| .resetfn = arm_cp_reset_ignore }, |
| { .name = "TPIDR_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 0, .opc2 = 4, .crn = 13, .crm = 0, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.tpidr_el[1]), .resetvalue = 0 }, |
| { .name = "TPIDRPRW", .opc1 = 0, .cp = 15, .crn = 13, .crm = 0, .opc2 = 4, |
| .access = PL1_RW, |
| .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tpidrprw_s), |
| offsetoflow32(CPUARMState, cp15.tpidrprw_ns) }, |
| .resetvalue = 0 }, |
| REGINFO_SENTINEL |
| }; |
| |
| #ifndef CONFIG_USER_ONLY |
| |
| static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero. |
| * Writable only at the highest implemented exception level. |
| */ |
| int el = arm_current_el(env); |
| uint64_t hcr; |
| uint32_t cntkctl; |
| |
| switch (el) { |
| case 0: |
| hcr = arm_hcr_el2_eff(env); |
| if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { |
| cntkctl = env->cp15.cnthctl_el2; |
| } else { |
| cntkctl = env->cp15.c14_cntkctl; |
| } |
| if (!extract32(cntkctl, 0, 2)) { |
| return CP_ACCESS_TRAP; |
| } |
| break; |
| case 1: |
| if (!isread && ri->state == ARM_CP_STATE_AA32 && |
| arm_is_secure_below_el3(env)) { |
| /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */ |
| return CP_ACCESS_TRAP_UNCATEGORIZED; |
| } |
| break; |
| case 2: |
| case 3: |
| break; |
| } |
| |
| if (!isread && el < arm_highest_el(env)) { |
| return CP_ACCESS_TRAP_UNCATEGORIZED; |
| } |
| |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult gt_counter_access(CPUARMState *env, int timeridx, |
| bool isread) |
| { |
| unsigned int cur_el = arm_current_el(env); |
| bool secure = arm_is_secure(env); |
| uint64_t hcr = arm_hcr_el2_eff(env); |
| |
| switch (cur_el) { |
| case 0: |
| /* If HCR_EL2.<E2H,TGE> == '11': check CNTHCTL_EL2.EL0[PV]CTEN. */ |
| if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { |
| return (extract32(env->cp15.cnthctl_el2, timeridx, 1) |
| ? CP_ACCESS_OK : CP_ACCESS_TRAP_EL2); |
| } |
| |
| /* CNT[PV]CT: not visible from PL0 if EL0[PV]CTEN is zero */ |
| if (!extract32(env->cp15.c14_cntkctl, timeridx, 1)) { |
| return CP_ACCESS_TRAP; |
| } |
| |
| /* If HCR_EL2.<E2H,TGE> == '10': check CNTHCTL_EL2.EL1PCTEN. */ |
| if (hcr & HCR_E2H) { |
| if (timeridx == GTIMER_PHYS && |
| !extract32(env->cp15.cnthctl_el2, 10, 1)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| } else { |
| /* If HCR_EL2.<E2H> == 0: check CNTHCTL_EL2.EL1PCEN. */ |
| if (arm_feature(env, ARM_FEATURE_EL2) && |
| timeridx == GTIMER_PHYS && !secure && |
| !extract32(env->cp15.cnthctl_el2, 1, 1)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| } |
| break; |
| |
| case 1: |
| /* Check CNTHCTL_EL2.EL1PCTEN, which changes location based on E2H. */ |
| if (arm_feature(env, ARM_FEATURE_EL2) && |
| |