| #include "qemu/osdep.h" |
| #include "cpu.h" |
| #include "internals.h" |
| #include "exec/gdbstub.h" |
| #include "exec/helper-proto.h" |
| #include "qemu/host-utils.h" |
| #include "sysemu/arch_init.h" |
| #include "sysemu/sysemu.h" |
| #include "qemu/bitops.h" |
| #include "qemu/crc32c.h" |
| #include "exec/exec-all.h" |
| #include "exec/cpu_ldst.h" |
| #include "arm_ldst.h" |
| #include <zlib.h> /* For crc32 */ |
| #include "exec/semihost.h" |
| #include "sysemu/kvm.h" |
| |
| #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ |
| |
| #ifndef CONFIG_USER_ONLY |
| static bool get_phys_addr(CPUARMState *env, target_ulong address, |
| int access_type, ARMMMUIdx mmu_idx, |
| hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, |
| target_ulong *page_size, uint32_t *fsr, |
| ARMMMUFaultInfo *fi); |
| |
| static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, |
| int access_type, ARMMMUIdx mmu_idx, |
| hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, |
| target_ulong *page_size_ptr, uint32_t *fsr, |
| ARMMMUFaultInfo *fi); |
| |
| /* Definitions for the PMCCNTR and PMCR registers */ |
| #define PMCRD 0x8 |
| #define PMCRC 0x4 |
| #define PMCRE 0x1 |
| #endif |
| |
| static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| int nregs; |
| |
| /* VFP data registers are always little-endian. */ |
| nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16; |
| if (reg < nregs) { |
| stfq_le_p(buf, env->vfp.regs[reg]); |
| return 8; |
| } |
| if (arm_feature(env, ARM_FEATURE_NEON)) { |
| /* Aliases for Q regs. */ |
| nregs += 16; |
| if (reg < nregs) { |
| stfq_le_p(buf, env->vfp.regs[(reg - 32) * 2]); |
| stfq_le_p(buf + 8, env->vfp.regs[(reg - 32) * 2 + 1]); |
| return 16; |
| } |
| } |
| switch (reg - nregs) { |
| case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4; |
| case 1: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSCR]); return 4; |
| case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4; |
| } |
| return 0; |
| } |
| |
| static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) |
| { |
| int nregs; |
| |
| nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16; |
| if (reg < nregs) { |
| env->vfp.regs[reg] = ldfq_le_p(buf); |
| return 8; |
| } |
| if (arm_feature(env, ARM_FEATURE_NEON)) { |
| nregs += 16; |
| if (reg < nregs) { |
| env->vfp.regs[(reg - 32) * 2] = ldfq_le_p(buf); |
| env->vfp.regs[(reg - 32) * 2 + 1] = ldfq_le_p(buf + 8); |
| return 16; |
| } |
| } |
| switch (reg - nregs) { |
| case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4; |
| case 1: env->vfp.xregs[ARM_VFP_FPSCR] = 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, uint8_t *buf, int reg) |
| { |
| switch (reg) { |
| case 0 ... 31: |
| /* 128 bit FP register */ |
| stfq_le_p(buf, env->vfp.regs[reg * 2]); |
| stfq_le_p(buf + 8, env->vfp.regs[reg * 2 + 1]); |
| return 16; |
| case 32: |
| /* FPSR */ |
| stl_p(buf, vfp_get_fpsr(env)); |
| return 4; |
| case 33: |
| /* FPCR */ |
| stl_p(buf, vfp_get_fpcr(env)); |
| return 4; |
| 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 */ |
| env->vfp.regs[reg * 2] = ldfq_le_p(buf); |
| env->vfp.regs[reg * 2 + 1] = ldfq_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); |
| } |
| } |
| |
| 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) |
| { |
| /* 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; |
| |
| ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); |
| if (!ri) { |
| ok = false; |
| continue; |
| } |
| if (ri->type & ARM_CP_NO_RAW) { |
| continue; |
| } |
| cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri); |
| } |
| 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); |
| |
| if (el < 2 && (env->cp15.mdcr_el2 & MDCR_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); |
| |
| if (el < 2 && (env->cp15.mdcr_el2 & MDCR_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); |
| |
| if (el < 2 && (env->cp15.mdcr_el2 & MDCR_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; |
| } |
| |
| static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| raw_write(env, ri, value); |
| tlb_flush(CPU(cpu), 1); /* Flush TLB as domain not tracked in TLB */ |
| } |
| |
| static void fcse_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(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), 1); |
| raw_write(env, ri, value); |
| } |
| } |
| |
| static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| if (raw_read(env, ri) != value && !arm_feature(env, ARM_FEATURE_MPU) |
| && !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), 1); |
| } |
| raw_write(env, ri, value); |
| } |
| |
| static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate all (TLBIALL) */ |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| tlb_flush(CPU(cpu), 1); |
| } |
| |
| static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); |
| } |
| |
| static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate by ASID (TLBIASID) */ |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| tlb_flush(CPU(cpu), value == 0); |
| } |
| |
| static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); |
| } |
| |
| /* IS variants of TLB operations must affect all cores */ |
| static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush(other_cs, 1); |
| } |
| } |
| |
| static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush(other_cs, value == 0); |
| } |
| } |
| |
| static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_page(other_cs, value & TARGET_PAGE_MASK); |
| } |
| } |
| |
| static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_page(other_cs, value & TARGET_PAGE_MASK); |
| } |
| } |
| |
| static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = ENV_GET_CPU(env); |
| |
| tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, |
| ARMMMUIdx_S2NS, -1); |
| } |
| |
| static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1, |
| ARMMMUIdx_S12NSE0, ARMMMUIdx_S2NS, -1); |
| } |
| } |
| |
| 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_GET_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, ARMMMUIdx_S2NS, -1); |
| } |
| |
| static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| uint64_t pageaddr; |
| |
| if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) { |
| return; |
| } |
| |
| pageaddr = sextract64(value << 12, 0, 40); |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S2NS, -1); |
| } |
| } |
| |
| static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = ENV_GET_CPU(env); |
| |
| tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E2, -1); |
| } |
| |
| static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E2, -1); |
| } |
| } |
| |
| static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *cs = ENV_GET_CPU(env); |
| uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); |
| |
| tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E2, -1); |
| } |
| |
| static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| CPUState *other_cs; |
| uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12); |
| |
| CPU_FOREACH(other_cs) { |
| tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E2, -1); |
| } |
| } |
| |
| 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(NS)", |
| .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, .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, .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, .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 (arm_feature(env, ARM_FEATURE_VFP)) { |
| /* 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 (!arm_feature(env, ARM_FEATURE_NEON) || |
| !arm_feature(env, ARM_FEATURE_VFP3)) { |
| /* D32DIS [30] is RAO/WI if D16-31 are not implemented. */ |
| value |= (1 << 30); |
| } |
| } |
| value &= mask; |
| } |
| env->cp15.cpacr_el1 = value; |
| } |
| |
| 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, |
| .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), |
| .resetvalue = 0, .writefn = cpacr_write }, |
| REGINFO_SENTINEL |
| }; |
| |
| 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) { |
| 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; |
| } |
| |
| #ifndef CONFIG_USER_ONLY |
| |
| static inline bool arm_ccnt_enabled(CPUARMState *env) |
| { |
| /* This does not support checking PMCCFILTR_EL0 register */ |
| |
| if (!(env->cp15.c9_pmcr & PMCRE)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void pmccntr_sync(CPUARMState *env) |
| { |
| uint64_t temp_ticks; |
| |
| temp_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), |
| ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); |
| |
| if (env->cp15.c9_pmcr & PMCRD) { |
| /* Increment once every 64 processor clock cycles */ |
| temp_ticks /= 64; |
| } |
| |
| if (arm_ccnt_enabled(env)) { |
| env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt; |
| } |
| } |
| |
| static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmccntr_sync(env); |
| |
| if (value & PMCRC) { |
| /* The counter has been reset */ |
| env->cp15.c15_ccnt = 0; |
| } |
| |
| /* only the DP, X, D and E bits are writable */ |
| env->cp15.c9_pmcr &= ~0x39; |
| env->cp15.c9_pmcr |= (value & 0x39); |
| |
| pmccntr_sync(env); |
| } |
| |
| static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint64_t total_ticks; |
| |
| if (!arm_ccnt_enabled(env)) { |
| /* Counter is disabled, do not change value */ |
| return env->cp15.c15_ccnt; |
| } |
| |
| total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), |
| ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); |
| |
| if (env->cp15.c9_pmcr & PMCRD) { |
| /* Increment once every 64 processor clock cycles */ |
| total_ticks /= 64; |
| } |
| return total_ticks - env->cp15.c15_ccnt; |
| } |
| |
| static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| uint64_t total_ticks; |
| |
| if (!arm_ccnt_enabled(env)) { |
| /* Counter is disabled, set the absolute value */ |
| env->cp15.c15_ccnt = value; |
| return; |
| } |
| |
| total_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), |
| ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); |
| |
| if (env->cp15.c9_pmcr & PMCRD) { |
| /* Increment once every 64 processor clock cycles */ |
| total_ticks /= 64; |
| } |
| env->cp15.c15_ccnt = total_ticks - value; |
| } |
| |
| 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)); |
| } |
| |
| #else /* CONFIG_USER_ONLY */ |
| |
| void pmccntr_sync(CPUARMState *env) |
| { |
| } |
| |
| #endif |
| |
| static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| pmccntr_sync(env); |
| env->cp15.pmccfiltr_el0 = value & 0x7E000000; |
| pmccntr_sync(env); |
| } |
| |
| static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= (1 << 31); |
| env->cp15.c9_pmcnten |= value; |
| } |
| |
| static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= (1 << 31); |
| env->cp15.c9_pmcnten &= ~value; |
| } |
| |
| static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| env->cp15.c9_pmovsr &= ~value; |
| } |
| |
| static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| env->cp15.c9_pmxevtyper = value & 0xff; |
| } |
| |
| static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| 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 &= (1 << 31); |
| env->cp15.c9_pminten |= value; |
| } |
| |
| static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| value &= (1 << 31); |
| env->cp15.c9_pminten &= ~value; |
| } |
| |
| 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) |
| { |
| /* We only mask off bits that are RES0 both for AArch64 and AArch32. |
| * For bits that vary between AArch32/64, code needs to check the |
| * current execution mode before directly using the feature bit. |
| */ |
| uint32_t valid_mask = SCR_AARCH64_MASK | SCR_AARCH32_MASK; |
| |
| 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; |
| } |
| } |
| |
| /* Clear all-context RES0 bits. */ |
| value &= valid_mask; |
| raw_write(env, ri, value); |
| } |
| |
| static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(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_GET_CPU(env); |
| uint64_t ret = 0; |
| |
| if (cs->interrupt_request & CPU_INTERRUPT_HARD) { |
| ret |= CPSR_I; |
| } |
| 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 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 (although we don't actually implement any counters) |
| * |
| * 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, .fieldoffset = offsetof(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, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), |
| .writefn = pmovsr_write, |
| .raw_writefn = raw_write }, |
| /* Unimplemented so WI. */ |
| { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, |
| .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP }, |
| /* Since we don't implement any events, writing to PMSELR is UNPREDICTABLE. |
| * We choose to RAZ/WI. |
| */ |
| { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, |
| .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0, |
| .accessfn = pmreg_access }, |
| #ifndef CONFIG_USER_ONLY |
| { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, |
| .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO, |
| .readfn = pmccntr_read, .writefn = pmccntr_write32, |
| .accessfn = pmreg_access }, |
| { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, |
| .access = PL0_RW, .accessfn = pmreg_access, |
| .type = ARM_CP_IO, |
| .readfn = pmccntr_read, .writefn = pmccntr_write, }, |
| #endif |
| { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, |
| .writefn = pmccfiltr_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, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pmxevtyper), |
| .accessfn = pmreg_access, .writefn = pmxevtyper_write, |
| .raw_writefn = raw_write }, |
| /* Unimplemented, RAZ/WI. */ |
| { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, |
| .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0, |
| .accessfn = pmreg_access }, |
| { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, |
| .access = PL0_R | PL1_RW, .accessfn = access_tpm, |
| .fieldoffset = offsetof(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, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), |
| .resetvalue = 0, |
| .writefn = pmintenset_write, .raw_writefn = raw_write }, |
| { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, |
| .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, |
| .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, |
| .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), |
| .writefn = pmintenclr_write }, |
| { .name = "VBAR", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, |
| .access = PL1_RW, .writefn = vbar_write, |
| .bank_fieldoffsets = { offsetof(CPUARMState, cp15.vbar_s), |
| offsetof(CPUARMState, cp15.vbar_ns) }, |
| .resetvalue = 0 }, |
| { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, |
| .access = PL1_R, .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, .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, .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, .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, .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, .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, |
| .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, |
| .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, .writefn = tlbiall_write }, |
| { .name = "ITLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, |
| { .name = "ITLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 5, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .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, .writefn = tlbiall_write }, |
| { .name = "DTLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, |
| { .name = "DTLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .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, .writefn = tlbiall_write }, |
| { .name = "TLBIMVA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, |
| { .name = "TLBIASID", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbiasid_write }, |
| { .name = "TLBIMVAA", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .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, .writefn = tlbiall_is_write }, |
| { .name = "TLBIMVAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, |
| { .name = "TLBIASIDIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, |
| .writefn = tlbiasid_is_write }, |
| { .name = "TLBIMVAAIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, |
| .type = ARM_CP_NO_RAW, .access = PL1_W, |
| .writefn = tlbimvaa_is_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); |
| |
| switch (el) { |
| case 0: |
| if (!extract32(env->cp15.c14_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); |
| |
| /* CNT[PV]CT: not visible from PL0 if ELO[PV]CTEN is zero */ |
| if (cur_el == 0 && |
| !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { |
| return CP_ACCESS_TRAP; |
| } |
| |
| if (arm_feature(env, ARM_FEATURE_EL2) && |
| timeridx == GTIMER_PHYS && !secure && cur_el < 2 && |
| !extract32(env->cp15.cnthctl_el2, 0, 1)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult gt_timer_access(CPUARMState *env, int timeridx, |
| bool isread) |
| { |
| unsigned int cur_el = arm_current_el(env); |
| bool secure = arm_is_secure(env); |
| |
| /* CNT[PV]_CVAL, CNT[PV]_CTL, CNT[PV]_TVAL: not visible from PL0 if |
| * EL0[PV]TEN is zero. |
| */ |
| if (cur_el == 0 && |
| !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { |
| return CP_ACCESS_TRAP; |
| } |
| |
| if (arm_feature(env, ARM_FEATURE_EL2) && |
| timeridx == GTIMER_PHYS && !secure && cur_el < 2 && |
| !extract32(env->cp15.cnthctl_el2, 1, 1)) { |
| return CP_ACCESS_TRAP_EL2; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static CPAccessResult gt_pct_access(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| return gt_counter_access(env, GTIMER_PHYS, isread); |
| } |
| |
| static CPAccessResult gt_vct_access(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| return gt_counter_access(env, GTIMER_VIRT, isread); |
| } |
| |
| static CPAccessResult gt_ptimer_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| return gt_timer_access(env, GTIMER_PHYS, isread); |
| } |
| |
| static CPAccessResult gt_vtimer_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| return gt_timer_access(env, GTIMER_VIRT, isread); |
| } |
| |
| static CPAccessResult gt_stimer_access(CPUARMState *env, |
| const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| /* The AArch64 register view of the secure physical timer is |
| * always accessible from EL3, and configurably accessible from |
| * Secure EL1. |
| */ |
| switch (arm_current_el(env)) { |
| case 1: |
| if (!arm_is_secure(env)) { |
| return CP_ACCESS_TRAP; |
| } |
| if (!(env->cp15.scr_el3 & SCR_ST)) { |
| return CP_ACCESS_TRAP_EL3; |
| } |
| return CP_ACCESS_OK; |
| case 0: |
| case 2: |
| return CP_ACCESS_TRAP; |
| case 3: |
| return CP_ACCESS_OK; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static uint64_t gt_get_countervalue(CPUARMState *env) |
| { |
| return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / GTIMER_SCALE; |
| } |
| |
| static void gt_recalc_timer(ARMCPU *cpu, int timeridx) |
| { |
| ARMGenericTimer *gt = &cpu->env.cp15.c14_timer[timeridx]; |
| |
| if (gt->ctl & 1) { |
| /* Timer enabled: calculate and set current ISTATUS, irq, and |
| * reset timer to when ISTATUS next has to change |
| */ |
| uint64_t offset = timeridx == GTIMER_VIRT ? |
| cpu->env.cp15.cntvoff_el2 : 0; |
| uint64_t count = gt_get_countervalue(&cpu->env); |
| /* Note that this must be unsigned 64 bit arithmetic: */ |
| int istatus = count - offset >= gt->cval; |
| uint64_t nexttick; |
| |
| gt->ctl = deposit32(gt->ctl, 2, 1, istatus); |
| qemu_set_irq(cpu->gt_timer_outputs[timeridx], |
| (istatus && !(gt->ctl & 2))); |
| if (istatus) { |
| /* Next transition is when count rolls back over to zero */ |
| nexttick = UINT64_MAX; |
| } else { |
| /* Next transition is when we hit cval */ |
| nexttick = gt->cval + offset; |
| } |
| /* Note that the desired next expiry time might be beyond the |
| * signed-64-bit range of a QEMUTimer -- in this case we just |
| * set the timer for as far in the future as possible. When the |
| * timer expires we will reset the timer for any remaining period. |
| */ |
| if (nexttick > INT64_MAX / GTIMER_SCALE) { |
| nexttick = INT64_MAX / GTIMER_SCALE; |
| } |
| timer_mod(cpu->gt_timer[timeridx], nexttick); |
| } else { |
| /* Timer disabled: ISTATUS and timer output always clear */ |
| gt->ctl &= ~4; |
| qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0); |
| timer_del(cpu->gt_timer[timeridx]); |
| } |
| } |
| |
| static void gt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri, |
| int timeridx) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| timer_del(cpu->gt_timer[timeridx]); |
| } |
| |
| static uint64_t gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_get_countervalue(env); |
| } |
| |
| static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_get_countervalue(env) - env->cp15.cntvoff_el2; |
| } |
| |
| static void gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| int timeridx, |
| uint64_t value) |
| { |
| env->cp15.c14_timer[timeridx].cval = value; |
| gt_recalc_timer(arm_env_get_cpu(env), timeridx); |
| } |
| |
| static uint64_t gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, |
| int timeridx) |
| { |
| uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; |
| |
| return (uint32_t)(env->cp15.c14_timer[timeridx].cval - |
| (gt_get_countervalue(env) - offset)); |
| } |
| |
| static void gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| int timeridx, |
| uint64_t value) |
| { |
| uint64_t offset = timeridx == GTIMER_VIRT ? env->cp15.cntvoff_el2 : 0; |
| |
| env->cp15.c14_timer[timeridx].cval = gt_get_countervalue(env) - offset + |
| sextract64(value, 0, 32); |
| gt_recalc_timer(arm_env_get_cpu(env), timeridx); |
| } |
| |
| static void gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| int timeridx, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| uint32_t oldval = env->cp15.c14_timer[timeridx].ctl; |
| |
| env->cp15.c14_timer[timeridx].ctl = deposit64(oldval, 0, 2, value); |
| if ((oldval ^ value) & 1) { |
| /* Enable toggled */ |
| gt_recalc_timer(cpu, timeridx); |
| } else if ((oldval ^ value) & 2) { |
| /* IMASK toggled: don't need to recalculate, |
| * just set the interrupt line based on ISTATUS |
| */ |
| qemu_set_irq(cpu->gt_timer_outputs[timeridx], |
| (oldval & 4) && !(value & 2)); |
| } |
| } |
| |
| static void gt_phys_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| gt_timer_reset(env, ri, GTIMER_PHYS); |
| } |
| |
| static void gt_phys_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_cval_write(env, ri, GTIMER_PHYS, value); |
| } |
| |
| static uint64_t gt_phys_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_tval_read(env, ri, GTIMER_PHYS); |
| } |
| |
| static void gt_phys_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_tval_write(env, ri, GTIMER_PHYS, value); |
| } |
| |
| static void gt_phys_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_ctl_write(env, ri, GTIMER_PHYS, value); |
| } |
| |
| static void gt_virt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| gt_timer_reset(env, ri, GTIMER_VIRT); |
| } |
| |
| static void gt_virt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_cval_write(env, ri, GTIMER_VIRT, value); |
| } |
| |
| static uint64_t gt_virt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_tval_read(env, ri, GTIMER_VIRT); |
| } |
| |
| static void gt_virt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_tval_write(env, ri, GTIMER_VIRT, value); |
| } |
| |
| static void gt_virt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_ctl_write(env, ri, GTIMER_VIRT, value); |
| } |
| |
| static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| raw_write(env, ri, value); |
| gt_recalc_timer(cpu, GTIMER_VIRT); |
| } |
| |
| static void gt_hyp_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| gt_timer_reset(env, ri, GTIMER_HYP); |
| } |
| |
| static void gt_hyp_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_cval_write(env, ri, GTIMER_HYP, value); |
| } |
| |
| static uint64_t gt_hyp_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_tval_read(env, ri, GTIMER_HYP); |
| } |
| |
| static void gt_hyp_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_tval_write(env, ri, GTIMER_HYP, value); |
| } |
| |
| static void gt_hyp_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_ctl_write(env, ri, GTIMER_HYP, value); |
| } |
| |
| static void gt_sec_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| gt_timer_reset(env, ri, GTIMER_SEC); |
| } |
| |
| static void gt_sec_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_cval_write(env, ri, GTIMER_SEC, value); |
| } |
| |
| static uint64_t gt_sec_tval_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return gt_tval_read(env, ri, GTIMER_SEC); |
| } |
| |
| static void gt_sec_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_tval_write(env, ri, GTIMER_SEC, value); |
| } |
| |
| static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| gt_ctl_write(env, ri, GTIMER_SEC, value); |
| } |
| |
| void arm_gt_ptimer_cb(void *opaque) |
| { |
| ARMCPU *cpu = opaque; |
| |
| gt_recalc_timer(cpu, GTIMER_PHYS); |
| } |
| |
| void arm_gt_vtimer_cb(void *opaque) |
| { |
| ARMCPU *cpu = opaque; |
| |
| gt_recalc_timer(cpu, GTIMER_VIRT); |
| } |
| |
| void arm_gt_htimer_cb(void *opaque) |
| { |
| ARMCPU *cpu = opaque; |
| |
| gt_recalc_timer(cpu, GTIMER_HYP); |
| } |
| |
| void arm_gt_stimer_cb(void *opaque) |
| { |
| ARMCPU *cpu = opaque; |
| |
| gt_recalc_timer(cpu, GTIMER_SEC); |
| } |
| |
| static const ARMCPRegInfo generic_timer_cp_reginfo[] = { |
| /* Note that CNTFRQ is purely reads-as-written for the benefit |
| * of software; writing it doesn't actually change the timer frequency. |
| * Our reset value matches the fixed frequency we implement the timer at. |
| */ |
| { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, |
| .type = ARM_CP_ALIAS, |
| .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, |
| .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq), |
| }, |
| { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, |
| .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), |
| .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE, |
| }, |
| /* overall control: mostly access permissions */ |
| { .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH, |
| .opc0 = 3, .opc1 = 0, .crn = 14, .crm = 1, .opc2 = 0, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl), |
| .resetvalue = 0, |
| }, |
| /* per-timer control */ |
| { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, |
| .secure = ARM_CP_SECSTATE_NS, |
| .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, |
| .fieldoffset = offsetoflow32(CPUARMState, |
| cp15.c14_timer[GTIMER_PHYS].ctl), |
| .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTP_CTL(S)", |
| .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, |
| .secure = ARM_CP_SECSTATE_S, |
| .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, |
| .fieldoffset = offsetoflow32(CPUARMState, |
| cp15.c14_timer[GTIMER_SEC].ctl), |
| .writefn = gt_sec_ctl_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, |
| .type = ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), |
| .resetvalue = 0, |
| .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1, |
| .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, |
| .accessfn = gt_vtimer_access, |
| .fieldoffset = offsetoflow32(CPUARMState, |
| cp15.c14_timer[GTIMER_VIRT].ctl), |
| .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, |
| .type = ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_vtimer_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), |
| .resetvalue = 0, |
| .writefn = gt_virt_ctl_write, .raw_writefn = raw_write, |
| }, |
| /* TimerValue views: a 32 bit downcounting view of the underlying state */ |
| { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, |
| .secure = ARM_CP_SECSTATE_NS, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, |
| .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, |
| }, |
| { .name = "CNTP_TVAL(S)", |
| .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, |
| .secure = ARM_CP_SECSTATE_S, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, |
| .readfn = gt_sec_tval_read, .writefn = gt_sec_tval_write, |
| }, |
| { .name = "CNTP_TVAL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_ptimer_access, .resetfn = gt_phys_timer_reset, |
| .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, |
| }, |
| { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_vtimer_access, |
| .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, |
| }, |
| { .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, |
| .accessfn = gt_vtimer_access, .resetfn = gt_virt_timer_reset, |
| .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, |
| }, |
| /* The counter itself */ |
| { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, |
| .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = gt_pct_access, |
| .readfn = gt_cnt_read, .resetfn = arm_cp_reset_ignore, |
| }, |
| { .name = "CNTPCT_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 1, |
| .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = gt_pct_access, .readfn = gt_cnt_read, |
| }, |
| { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, |
| .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = gt_vct_access, |
| .readfn = gt_virt_cnt_read, .resetfn = arm_cp_reset_ignore, |
| }, |
| { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, |
| .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, |
| .accessfn = gt_vct_access, .readfn = gt_virt_cnt_read, |
| }, |
| /* Comparison value, indicating when the timer goes off */ |
| { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, |
| .secure = ARM_CP_SECSTATE_NS, |
| .access = PL1_RW | PL0_R, |
| .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), |
| .accessfn = gt_ptimer_access, |
| .writefn = gt_phys_cval_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTP_CVAL(S)", .cp = 15, .crm = 14, .opc1 = 2, |
| .secure = ARM_CP_SECSTATE_S, |
| .access = PL1_RW | PL0_R, |
| .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), |
| .accessfn = gt_ptimer_access, |
| .writefn = gt_sec_cval_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, |
| .access = PL1_RW | PL0_R, |
| .type = ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), |
| .resetvalue = 0, .accessfn = gt_ptimer_access, |
| .writefn = gt_phys_cval_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, |
| .access = PL1_RW | PL0_R, |
| .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), |
| .accessfn = gt_vtimer_access, |
| .writefn = gt_virt_cval_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, |
| .access = PL1_RW | PL0_R, |
| .type = ARM_CP_IO, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), |
| .resetvalue = 0, .accessfn = gt_vtimer_access, |
| .writefn = gt_virt_cval_write, .raw_writefn = raw_write, |
| }, |
| /* Secure timer -- this is actually restricted to only EL3 |
| * and configurably Secure-EL1 via the accessfn. |
| */ |
| { .name = "CNTPS_TVAL_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 0, |
| .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW, |
| .accessfn = gt_stimer_access, |
| .readfn = gt_sec_tval_read, |
| .writefn = gt_sec_tval_write, |
| .resetfn = gt_sec_timer_reset, |
| }, |
| { .name = "CNTPS_CTL_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 1, |
| .type = ARM_CP_IO, .access = PL1_RW, |
| .accessfn = gt_stimer_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].ctl), |
| .resetvalue = 0, |
| .writefn = gt_sec_ctl_write, .raw_writefn = raw_write, |
| }, |
| { .name = "CNTPS_CVAL_EL1", .state = ARM_CP_STATE_AA64, |
| .opc0 = 3, .opc1 = 7, .crn = 14, .crm = 2, .opc2 = 2, |
| .type = ARM_CP_IO, .access = PL1_RW, |
| .accessfn = gt_stimer_access, |
| .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), |
| .writefn = gt_sec_cval_write, .raw_writefn = raw_write, |
| }, |
| REGINFO_SENTINEL |
| }; |
| |
| #else |
| /* In user-mode none of the generic timer registers are accessible, |
| * and their implementation depends on QEMU_CLOCK_VIRTUAL and qdev gpio outputs, |
| * so instead just don't register any of them. |
| */ |
| static const ARMCPRegInfo generic_timer_cp_reginfo[] = { |
| REGINFO_SENTINEL |
| }; |
| |
| #endif |
| |
| static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| if (arm_feature(env, ARM_FEATURE_LPAE)) { |
| raw_write(env, ri, value); |
| } else if (arm_feature(env, ARM_FEATURE_V7)) { |
| raw_write(env, ri, value & 0xfffff6ff); |
| } else { |
| raw_write(env, ri, value & 0xfffff1ff); |
| } |
| } |
| |
| #ifndef CONFIG_USER_ONLY |
| /* get_phys_addr() isn't present for user-mode-only targets */ |
| |
| static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (ri->opc2 & 4) { |
| /* The ATS12NSO* operations must trap to EL3 if executed in |
| * Secure EL1 (which can only happen if EL3 is AArch64). |
| * They are simply UNDEF if executed from NS EL1. |
| * They function normally from EL2 or EL3. |
| */ |
| if (arm_current_el(env) == 1) { |
| if (arm_is_secure_below_el3(env)) { |
| return CP_ACCESS_TRAP_UNCATEGORIZED_EL3; |
| } |
| return CP_ACCESS_TRAP_UNCATEGORIZED; |
| } |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static uint64_t do_ats_write(CPUARMState *env, uint64_t value, |
| int access_type, ARMMMUIdx mmu_idx) |
| { |
| hwaddr phys_addr; |
| target_ulong page_size; |
| int prot; |
| uint32_t fsr; |
| bool ret; |
| uint64_t par64; |
| MemTxAttrs attrs = {}; |
| ARMMMUFaultInfo fi = {}; |
| |
| ret = get_phys_addr(env, value, access_type, mmu_idx, |
| &phys_addr, &attrs, &prot, &page_size, &fsr, &fi); |
| if (extended_addresses_enabled(env)) { |
| /* fsr is a DFSR/IFSR value for the long descriptor |
| * translation table format, but with WnR always clear. |
| * Convert it to a 64-bit PAR. |
| */ |
| par64 = (1 << 11); /* LPAE bit always set */ |
| if (!ret) { |
| par64 |= phys_addr & ~0xfffULL; |
| if (!attrs.secure) { |
| par64 |= (1 << 9); /* NS */ |
| } |
| /* We don't set the ATTR or SH fields in the PAR. */ |
| } else { |
| par64 |= 1; /* F */ |
| par64 |= (fsr & 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. |
| */ |
| } |
| } else { |
| /* fsr 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) { |
| /* We do not set any attribute bits in the PAR */ |
| if (page_size == (1 << 24) |
| && arm_feature(env, ARM_FEATURE_V7)) { |
| par64 = (phys_addr & 0xff000000) | (1 << 1); |
| } else { |
| par64 = phys_addr & 0xfffff000; |
| } |
| if (!attrs.secure) { |
| par64 |= (1 << 9); /* NS */ |
| } |
| } else { |
| par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) | |
| ((fsr & 0xf) << 1) | 1; |
| } |
| } |
| return par64; |
| } |
| |
| static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) |
| { |
| int access_type = ri->opc2 & 1; |
| uint64_t par64; |
| ARMMMUIdx mmu_idx; |
| int el = arm_current_el(env); |
| bool secure = arm_is_secure_below_el3(env); |
| |
| switch (ri->opc2 & 6) { |
| case 0: |
| /* stage 1 current state PL1: ATS1CPR, ATS1CPW */ |
| switch (el) { |
| case 3: |
| mmu_idx = ARMMMUIdx_S1E3; |
| break; |
| case 2: |
| mmu_idx = ARMMMUIdx_S1NSE1; |
| break; |
| case 1: |
| mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| break; |
| case 2: |
| /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ |
| switch (el) { |
| case 3: |
| mmu_idx = ARMMMUIdx_S1SE0; |
| break; |
| case 2: |
| mmu_idx = ARMMMUIdx_S1NSE0; |
| break; |
| case 1: |
| mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| break; |
| case 4: |
| /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ |
| mmu_idx = ARMMMUIdx_S12NSE1; |
| break; |
| case 6: |
| /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ |
| mmu_idx = ARMMMUIdx_S12NSE0; |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| |
| par64 = do_ats_write(env, value, access_type, mmu_idx); |
| |
| A32_BANKED_CURRENT_REG_SET(env, par, par64); |
| } |
| |
| static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| int access_type = ri->opc2 & 1; |
| uint64_t par64; |
| |
| par64 = do_ats_write(env, value, access_type, ARMMMUIdx_S2NS); |
| |
| A32_BANKED_CURRENT_REG_SET(env, par, par64); |
| } |
| |
| static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, |
| bool isread) |
| { |
| if (arm_current_el(env) == 3 && !(env->cp15.scr_el3 & SCR_NS)) { |
| return CP_ACCESS_TRAP; |
| } |
| return CP_ACCESS_OK; |
| } |
| |
| static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| int access_type = ri->opc2 & 1; |
| ARMMMUIdx mmu_idx; |
| int secure = arm_is_secure_below_el3(env); |
| |
| switch (ri->opc2 & 6) { |
| case 0: |
| switch (ri->opc1) { |
| case 0: /* AT S1E1R, AT S1E1W */ |
| mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S1NSE1; |
| break; |
| case 4: /* AT S1E2R, AT S1E2W */ |
| mmu_idx = ARMMMUIdx_S1E2; |
| break; |
| case 6: /* AT S1E3R, AT S1E3W */ |
| mmu_idx = ARMMMUIdx_S1E3; |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| break; |
| case 2: /* AT S1E0R, AT S1E0W */ |
| mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S1NSE0; |
| break; |
| case 4: /* AT S12E1R, AT S12E1W */ |
| mmu_idx = secure ? ARMMMUIdx_S1SE1 : ARMMMUIdx_S12NSE1; |
| break; |
| case 6: /* AT S12E0R, AT S12E0W */ |
| mmu_idx = secure ? ARMMMUIdx_S1SE0 : ARMMMUIdx_S12NSE0; |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| |
| env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx); |
| } |
| #endif |
| |
| static const ARMCPRegInfo vapa_cp_reginfo[] = { |
| { .name = "PAR", .cp = 15, .crn = 7, .crm = 4, .opc1 = 0, .opc2 = 0, |
| .access = PL1_RW, .resetvalue = 0, |
| .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), |
| offsetoflow32(CPUARMState, cp15.par_ns) }, |
| .writefn = par_write }, |
| #ifndef CONFIG_USER_ONLY |
| /* This underdecoding is safe because the reginfo is NO_RAW. */ |
| { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, |
| .access = PL1_W, .accessfn = ats_access, |
| .writefn = ats_write, .type = ARM_CP_NO_RAW }, |
| #endif |
| REGINFO_SENTINEL |
| }; |
| |
| /* Return basic MPU access permission bits. */ |
| static uint32_t simple_mpu_ap_bits(uint32_t val) |
| { |
| uint32_t ret; |
| uint32_t mask; |
| int i; |
| ret = 0; |
| mask = 3; |
| for (i = 0; i < 16; i += 2) { |
| ret |= (val >> i) & mask; |
| mask <<= 2; |
| } |
| return ret; |
| } |
| |
| /* Pad basic MPU access permission bits to extended format. */ |
| static uint32_t extended_mpu_ap_bits(uint32_t val) |
| { |
| uint32_t ret; |
| uint32_t mask; |
| int i; |
| ret = 0; |
| mask = 3; |
| for (i = 0; i < 16; i += 2) { |
| ret |= (val & mask) << i; |
| mask <<= 2; |
| } |
| return ret; |
| } |
| |
| static void pmsav5_data_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| env->cp15.pmsav5_data_ap = extended_mpu_ap_bits(value); |
| } |
| |
| static uint64_t pmsav5_data_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return simple_mpu_ap_bits(env->cp15.pmsav5_data_ap); |
| } |
| |
| static void pmsav5_insn_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| env->cp15.pmsav5_insn_ap = extended_mpu_ap_bits(value); |
| } |
| |
| static uint64_t pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| return simple_mpu_ap_bits(env->cp15.pmsav5_insn_ap); |
| } |
| |
| static uint64_t pmsav7_read(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri); |
| |
| if (!u32p) { |
| return 0; |
| } |
| |
| u32p += env->cp15.c6_rgnr; |
| return *u32p; |
| } |
| |
| static void pmsav7_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri); |
| |
| if (!u32p) { |
| return; |
| } |
| |
| u32p += env->cp15.c6_rgnr; |
| tlb_flush(CPU(cpu), 1); /* Mappings may have changed - purge! */ |
| *u32p = value; |
| } |
| |
| static void pmsav7_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| uint32_t *u32p = *(uint32_t **)raw_ptr(env, ri); |
| |
| if (!u32p) { |
| return; |
| } |
| |
| memset(u32p, 0, sizeof(*u32p) * cpu->pmsav7_dregion); |
| } |
| |
| static void pmsav7_rgnr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| uint32_t nrgs = cpu->pmsav7_dregion; |
| |
| if (value >= nrgs) { |
| qemu_log_mask(LOG_GUEST_ERROR, |
| "PMSAv7 RGNR write >= # supported regions, %" PRIu32 |
| " > %" PRIu32 "\n", (uint32_t)value, nrgs); |
| return; |
| } |
| |
| raw_write(env, ri, value); |
| } |
| |
| static const ARMCPRegInfo pmsav7_cp_reginfo[] = { |
| { .name = "DRBAR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 0, |
| .access = PL1_RW, .type = ARM_CP_NO_RAW, |
| .fieldoffset = offsetof(CPUARMState, pmsav7.drbar), |
| .readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset }, |
| { .name = "DRSR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 2, |
| .access = PL1_RW, .type = ARM_CP_NO_RAW, |
| .fieldoffset = offsetof(CPUARMState, pmsav7.drsr), |
| .readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset }, |
| { .name = "DRACR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 1, .opc2 = 4, |
| .access = PL1_RW, .type = ARM_CP_NO_RAW, |
| .fieldoffset = offsetof(CPUARMState, pmsav7.dracr), |
| .readfn = pmsav7_read, .writefn = pmsav7_write, .resetfn = pmsav7_reset }, |
| { .name = "RGNR", .cp = 15, .crn = 6, .opc1 = 0, .crm = 2, .opc2 = 0, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_rgnr), |
| .writefn = pmsav7_rgnr_write }, |
| REGINFO_SENTINEL |
| }; |
| |
| static const ARMCPRegInfo pmsav5_cp_reginfo[] = { |
| { .name = "DATA_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, |
| .access = PL1_RW, .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), |
| .readfn = pmsav5_data_ap_read, .writefn = pmsav5_data_ap_write, }, |
| { .name = "INSN_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1, |
| .access = PL1_RW, .type = ARM_CP_ALIAS, |
| .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), |
| .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, |
| { .name = "DATA_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 2, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), |
| .resetvalue = 0, }, |
| { .name = "INSN_EXT_AP", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 3, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), |
| .resetvalue = 0, }, |
| { .name = "DCACHE_CFG", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, |
| .access = PL1_RW, |
| .fieldoffset = offsetof(CPUARMState, cp15.c2_data), .resetvalue = 0, }, |
| { .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_PRBS0", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[0]) }, |
| { .name = "946_PRBS1", .cp = 15, .crn = 6, .crm = 1, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[1]) }, |
| { .name = "946_PRBS2", .cp = 15, .crn = 6, .crm = 2, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[2]) }, |
| { .name = "946_PRBS3", .cp = 15, .crn = 6, .crm = 3, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[3]) }, |
| { .name = "946_PRBS4", .cp = 15, .crn = 6, .crm = 4, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[4]) }, |
| { .name = "946_PRBS5", .cp = 15, .crn = 6, .crm = 5, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[5]) }, |
| { .name = "946_PRBS6", .cp = 15, .crn = 6, .crm = 6, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[6]) }, |
| { .name = "946_PRBS7", .cp = 15, .crn = 6, .crm = 7, .opc1 = 0, |
| .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, |
| .fieldoffset = offsetof(CPUARMState, cp15.c6_region[7]) }, |
| REGINFO_SENTINEL |
| }; |
| |
| static void vmsa_ttbcr_raw_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| TCR *tcr = raw_ptr(env, ri); |
| int maskshift = extract32(value, 0, 3); |
| |
| if (!arm_feature(env, ARM_FEATURE_V8)) { |
| if (arm_feature(env, ARM_FEATURE_LPAE) && (value & TTBCR_EAE)) { |
| /* Pre ARMv8 bits [21:19], [15:14] and [6:3] are UNK/SBZP when |
| * using Long-desciptor translation table format */ |
| value &= ~((7 << 19) | (3 << 14) | (0xf << 3)); |
| } else if (arm_feature(env, ARM_FEATURE_EL3)) { |
| /* In an implementation that includes the Security Extensions |
| * TTBCR has additional fields PD0 [4] and PD1 [5] for |
| * Short-descriptor translation table format. |
| */ |
| value &= TTBCR_PD1 | TTBCR_PD0 | TTBCR_N; |
| } else { |
| value &= TTBCR_N; |
| } |
| } |
| |
| /* Update the masks corresponding to the TCR bank being written |
| * Note that we always calculate mask and base_mask, but |
| * they are only used for short-descriptor tables (ie if EAE is 0); |
| * for long-descriptor tables the TCR fields are used differently |
| * and the mask and base_mask values are meaningless. |
| */ |
| tcr->raw_tcr = value; |
| tcr->mask = ~(((uint32_t)0xffffffffu) >> maskshift); |
| tcr->base_mask = ~((uint32_t)0x3fffu >> maskshift); |
| } |
| |
| static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| |
| if (arm_feature(env, ARM_FEATURE_LPAE)) { |
| /* With LPAE the TTBCR could result in a change of ASID |
| * via the TTBCR.A1 bit, so do a TLB flush. |
| */ |
| tlb_flush(CPU(cpu), 1); |
| } |
| vmsa_ttbcr_raw_write(env, ri, value); |
| } |
| |
| static void vmsa_ttbcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) |
| { |
| TCR *tcr = raw_ptr(env, ri); |
| |
| /* Reset both the TCR as well as the masks corresponding to the bank of |
| * the TCR being reset. |
| */ |
| tcr->raw_tcr = 0; |
| tcr->mask = 0; |
| tcr->base_mask = 0xffffc000u; |
| } |
| |
| static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| ARMCPU *cpu = arm_env_get_cpu(env); |
| TCR *tcr = raw_ptr(env, ri); |
| |
| /* For AArch64 the A1 bit could result in a change of ASID, so TLB flush. */ |
| tlb_flush(CPU(cpu), 1); |
| tcr->raw_tcr = value; |
| } |
| |
| static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, |
| uint64_t value) |
| { |
| /* 64 bit accesses to the TTBRs can change the ASID and so we |
| * must flush the TLB. |
| */ |
|