diff options
Diffstat (limited to 'target-arm/helper.c')
-rw-r--r-- | target-arm/helper.c | 818 |
1 files changed, 551 insertions, 267 deletions
diff --git a/target-arm/helper.c b/target-arm/helper.c index 1a5e0678b0..1a1a00577e 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -13,7 +13,7 @@ #ifndef CONFIG_USER_ONLY static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size); @@ -119,6 +119,7 @@ static int aarch64_fpu_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) 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 { @@ -129,6 +130,7 @@ static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *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 { @@ -174,6 +176,27 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } +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. */ @@ -189,7 +212,7 @@ bool write_cpustate_to_list(ARMCPU *cpu) ok = false; continue; } - if (ri->type & ARM_CP_NO_MIGRATE) { + if (ri->type & ARM_CP_NO_RAW) { continue; } cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri); @@ -212,7 +235,7 @@ bool write_list_to_cpustate(ARMCPU *cpu) ok = false; continue; } - if (ri->type & ARM_CP_NO_MIGRATE) { + if (ri->type & ARM_CP_NO_RAW) { continue; } /* Write value and confirm it reads back as written @@ -236,7 +259,7 @@ static void add_cpreg_to_list(gpointer key, gpointer opaque) regidx = *(uint32_t *)key; ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - if (!(ri->type & ARM_CP_NO_MIGRATE)) { + 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++; @@ -252,7 +275,7 @@ static void count_cpreg(gpointer key, gpointer opaque) regidx = *(uint32_t *)key; ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); - if (!(ri->type & ARM_CP_NO_MIGRATE)) { + if (!(ri->type & (ARM_CP_NO_RAW|ARM_CP_ALIAS))) { cpu->cpreg_array_len++; } } @@ -508,7 +531,7 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { .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_MIGRATE, + .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 @@ -522,16 +545,16 @@ static const ARMCPRegInfo not_v7_cp_reginfo[] = { */ { .name = "TLBIALL", .cp = 15, .crn = 8, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = 0, .access = PL1_W, .writefn = tlbiall_write, - .type = ARM_CP_NO_MIGRATE }, + .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_MIGRATE }, + .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_MIGRATE }, + .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_MIGRATE }, + .type = ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -854,7 +877,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { * 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_NO_MIGRATE, + .access = PL0_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, @@ -869,11 +892,11 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, .writefn = pmcntenclr_write, - .type = ARM_CP_NO_MIGRATE }, + .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_NO_MIGRATE, + .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, @@ -928,7 +951,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .resetvalue = 0, .writefn = pmintenset_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenclr_write, }, { .name = "VBAR", .state = ARM_CP_STATE_BOTH, @@ -939,7 +962,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .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_MIGRATE }, + .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, @@ -988,44 +1011,44 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .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_MIGRATE, .access = PL1_R, .readfn = isr_read }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiall_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiasid_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbiall_is_write }, + .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_MIGRATE, .access = PL1_W, .writefn = tlbimva_is_write }, + .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_MIGRATE, .access = PL1_W, + .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_MIGRATE, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_is_write }, REGINFO_SENTINEL }; @@ -1268,7 +1291,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { * 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_NO_MIGRATE, + .type = ARM_CP_ALIAS, .access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_cntfrq), .resetfn = arm_cp_reset_ignore, @@ -1288,7 +1311,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, /* per-timer control */ { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, - .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, .access = PL1_RW | PL0_R, + .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), @@ -1304,7 +1327,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .writefn = gt_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_NO_MIGRATE, .access = PL1_RW | PL0_R, + .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), @@ -1321,52 +1344,52 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, /* TimerValue views: a 32 bit downcounting view of the underlying state */ { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_ptimer_access, .readfn = gt_tval_read, .writefn = gt_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_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .readfn = gt_tval_read, .writefn = gt_tval_write, }, { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .accessfn = gt_vtimer_access, .readfn = gt_tval_read, .writefn = gt_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_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, .readfn = gt_tval_read, .writefn = gt_tval_write, }, /* The counter itself */ { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0, - .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .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_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_pct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1, - .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_vct_access, .readfn = gt_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_MIGRATE | ARM_CP_IO, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, .accessfn = gt_vct_access, .readfn = gt_cnt_read, .resetfn = gt_cnt_reset, }, /* Comparison value, indicating when the timer goes off */ { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .accessfn = gt_ptimer_access, .resetfn = arm_cp_reset_ignore, .writefn = gt_cval_write, .raw_writefn = raw_write, @@ -1381,7 +1404,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, .access = PL1_RW | PL0_R, - .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .accessfn = gt_vtimer_access, .resetfn = arm_cp_reset_ignore, .writefn = gt_cval_write, .raw_writefn = raw_write, @@ -1428,23 +1451,23 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri) /* Other states are only available with TrustZone; in * a non-TZ implementation these registers don't exist * at all, which is an Uncategorized trap. This underdecoding - * is safe because the reginfo is NO_MIGRATE. + * is safe because the reginfo is NO_RAW. */ return CP_ACCESS_TRAP_UNCATEGORIZED; } return CP_ACCESS_OK; } -static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +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; - int ret, is_user = ri->opc2 & 2; - int access_type = ri->opc2 & 1; + int ret; uint64_t par64; - ret = get_phys_addr(env, value, access_type, is_user, + ret = get_phys_addr(env, value, access_type, mmu_idx, &phys_addr, &prot, &page_size); if (extended_addresses_enabled(env)) { /* ret is a DFSR/IFSR value for the long descriptor @@ -1481,9 +1504,105 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) ((ret & 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 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 = ARMMMUIdx_S12NSE1; + break; + case 6: /* AT S12E0R, AT S12E0W */ + mmu_idx = 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[] = { @@ -1495,7 +1614,7 @@ static const ARMCPRegInfo vapa_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY { .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_MIGRATE }, + .writefn = ats_write, .type = ARM_CP_NO_RAW }, #endif REGINFO_SENTINEL }; @@ -1554,12 +1673,12 @@ static uint64_t pmsav5_insn_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) 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_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_data_ap), .resetvalue = 0, .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_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.pmsav5_insn_ap), .resetvalue = 0, .readfn = pmsav5_insn_ap_read, .writefn = pmsav5_insn_ap_write, }, @@ -1691,7 +1810,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_ALIAS, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s), offsetoflow32(CPUARMState, cp15.dfsr_ns) }, .resetfn = arm_cp_reset_ignore, }, @@ -1719,7 +1838,7 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[1]) }, { .name = "TTBCR", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .writefn = vmsa_ttbcr_write, + .access = PL1_RW, .type = ARM_CP_ALIAS, .writefn = vmsa_ttbcr_write, .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, @@ -1789,7 +1908,7 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .writefn = omap_threadid_write }, { .name = "TI925T_STATUS", .cp = 15, .crn = 15, .crm = 8, .opc1 = 0, .opc2 = 0, .access = PL1_RW, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .readfn = arm_cp_read_zero, .writefn = omap_wfi_write, }, /* TODO: Peripheral port remap register: * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt controller @@ -1798,7 +1917,7 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { */ { .name = "OMAP_CACHEMAINT", .cp = 15, .crn = 7, .crm = CP_ANY, .opc1 = 0, .opc2 = CP_ANY, .access = PL1_W, - .type = ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE, + .type = ARM_CP_OVERRIDE | ARM_CP_NO_RAW, .writefn = omap_cachemaint_write }, { .name = "C9", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, @@ -1848,7 +1967,7 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { { .name = "C15_IMPDEF", .cp = 15, .crn = 15, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, - .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE | ARM_CP_OVERRIDE, + .type = ARM_CP_CONST | ARM_CP_NO_RAW | ARM_CP_OVERRIDE, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -1856,7 +1975,7 @@ static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { /* Cache status: RAZ because we have no cache so it's always clean */ { .name = "CDSR", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 6, - .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL1_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -1864,7 +1983,7 @@ static const ARMCPRegInfo cache_dirty_status_cp_reginfo[] = { static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { /* We never have a a block transfer operation in progress */ { .name = "BXSR", .cp = 15, .crn = 7, .crm = 12, .opc1 = 0, .opc2 = 4, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = 0 }, /* The cache ops themselves: these all NOP for QEMU */ { .name = "IICR", .cp = 15, .crm = 5, .opc1 = 0, @@ -1887,10 +2006,10 @@ static const ARMCPRegInfo cache_test_clean_cp_reginfo[] = { * to indicate that there are no dirty cache lines. */ { .name = "TC_DCACHE", .cp = 15, .crn = 7, .crm = 10, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = (1 << 30) }, { .name = "TCI_DCACHE", .cp = 15, .crn = 7, .crm = 14, .opc1 = 0, .opc2 = 3, - .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_CONST | ARM_CP_NO_RAW, .resetvalue = (1 << 30) }, REGINFO_SENTINEL }; @@ -1900,7 +2019,7 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { { .name = "C9_READBUFFER", .cp = 15, .crn = 9, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_RW, .resetvalue = 0, - .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_MIGRATE }, + .type = ARM_CP_CONST | ARM_CP_OVERRIDE | ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -1926,7 +2045,7 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) static const ARMCPRegInfo mpidr_cp_reginfo[] = { { .name = "MPIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, - .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_MIGRATE }, + .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, REGINFO_SENTINEL }; @@ -1947,12 +2066,12 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = { .bank_fieldoffsets = { offsetof(CPUARMState, cp15.par_s), offsetof(CPUARMState, cp15.par_ns)} }, { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr0_s), offsetof(CPUARMState, cp15.ttbr0_ns) }, .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1, - .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE, + .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .bank_fieldoffsets = { offsetof(CPUARMState, cp15.ttbr1_s), offsetof(CPUARMState, cp15.ttbr1_ns) }, .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore }, @@ -2144,7 +2263,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL0_RW, .type = ARM_CP_NZCV }, { .name = "DAIF", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 1, .crn = 4, .crm = 2, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .access = PL0_RW, .accessfn = aa64_daif_access, .fieldoffset = offsetof(CPUARMState, daif), .writefn = aa64_daif_write, .resetfn = arm_cp_reset_ignore }, @@ -2156,7 +2275,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .access = PL0_RW, .readfn = aa64_fpsr_read, .writefn = aa64_fpsr_write }, { .name = "DCZID_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .opc2 = 7, .crn = 0, .crm = 0, - .access = PL0_R, .type = ARM_CP_NO_MIGRATE, + .access = PL0_R, .type = ARM_CP_NO_RAW, .readfn = aa64_dczid_read }, { .name = "DC_ZVA", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 4, .opc2 = 1, @@ -2207,77 +2326,77 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* TLBI operations */ { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbiall_is_write }, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_is_write }, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_asid_is_write }, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_is_write }, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_is_write }, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_is_write }, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbiall_write }, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_write }, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_asid_write }, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_write }, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_va_write }, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = tlbi_aa64_vaa_write }, #ifndef CONFIG_USER_ONLY /* 64 bit address translation operations */ { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_MIGRATE, .writefn = ats_write }, + .access = PL1_W, .type = ARM_CP_NO_RAW, .writefn = ats_write64 }, #endif /* TLB invalidate last level of translation table walk */ { .name = "TLBIMVALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_is_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_is_write }, { .name = "TLBIMVAALIS", .cp = 15, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_is_write }, { .name = "TLBIMVAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimva_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimva_write }, { .name = "TLBIMVAAL", .cp = 15, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7, - .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .writefn = tlbimvaa_write }, + .type = ARM_CP_NO_RAW, .access = PL1_W, .writefn = tlbimvaa_write }, /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .type = ARM_CP_NOP, .access = PL1_W }, @@ -2312,12 +2431,12 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dacr_s), offsetoflow32(CPUARMState, cp15.dacr_ns) } }, { .name = "ELR_EL1", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, elr_el[1]) }, { .name = "SPSR_EL1", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[0]) }, /* We rely on the access checks not allowing the guest to write to the @@ -2327,11 +2446,15 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "SP_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 1, .opc2 = 0, .access = PL1_RW, .accessfn = sp_el0_access, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, sp_el[0]) }, + { .name = "SP_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 1, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, sp_el[1]) }, { .name = "SPSel", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 4, .crm = 2, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write }, REGINFO_SENTINEL }; @@ -2343,7 +2466,7 @@ static const ARMCPRegInfo v8_el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_NO_RAW, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, @@ -2386,12 +2509,12 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .writefn = dacr_write, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.dacr32_el2) }, { .name = "ELR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 1, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, elr_el[2]) }, { .name = "ESR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, { .name = "IFSR32_EL2", .state = ARM_CP_STATE_AA64, @@ -2402,7 +2525,7 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[6]) }, { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, @@ -2410,6 +2533,10 @@ static const ARMCPRegInfo v8_el2_cp_reginfo[] = { .access = PL2_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]), .resetvalue = 0 }, + { .name = "SP_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 1, .opc2 = 0, + .access = PL3_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, sp_el[2]) }, REGINFO_SENTINEL }; @@ -2418,7 +2545,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 1, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.scr_el3), .resetvalue = 0, .writefn = scr_write }, - { .name = "SCR", .type = ARM_CP_NO_MIGRATE, + { .name = "SCR", .type = ARM_CP_ALIAS, .cp = 15, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.scr_el3), .resetfn = arm_cp_reset_ignore, .writefn = scr_write }, @@ -2451,19 +2578,19 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .resetfn = vmsa_ttbcr_reset, .raw_writefn = raw_write, .fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) }, { .name = "ELR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 1, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, elr_el[3]) }, { .name = "ESR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 5, .crm = 2, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[3]) }, { .name = "FAR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 6, .crm = 0, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[3]) }, { .name = "SPSR_EL3", .state = ARM_CP_STATE_AA64, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 6, .crn = 4, .crm = 0, .opc2 = 0, .access = PL3_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[7]) }, { .name = "VBAR_EL3", .state = ARM_CP_STATE_AA64, @@ -2510,7 +2637,7 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { */ { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, - .type = ARM_CP_NO_MIGRATE, + .type = ARM_CP_ALIAS, .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetfn = arm_cp_reset_ignore }, @@ -2963,7 +3090,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, - .type = ARM_CP_IO | ARM_CP_NO_MIGRATE, + .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), .accessfn = pmreg_access, .writefn = pmcr_write, .raw_writefn = raw_write, @@ -3053,17 +3180,30 @@ void register_cp_regs_for_features(ARMCPU *cpu) .resetvalue = cpu->mvfr2 }, REGINFO_SENTINEL }; - ARMCPRegInfo rvbar = { - .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 2, - .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar - }; - define_one_arm_cp_reg(cpu, &rvbar); + /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ + if (!arm_feature(env, ARM_FEATURE_EL3) && + !arm_feature(env, ARM_FEATURE_EL2)) { + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL1_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); + } define_arm_cp_regs(cpu, v8_idregs); define_arm_cp_regs(cpu, v8_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_EL2)) { define_arm_cp_regs(cpu, v8_el2_cp_reginfo); + /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ + if (!arm_feature(env, ARM_FEATURE_EL3)) { + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL2_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); + } } else { /* If EL2 is missing but higher ELs are enabled, we need to * register the no_el2 reginfos. @@ -3074,6 +3214,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_EL3)) { define_arm_cp_regs(cpu, el3_cp_reginfo); + ARMCPRegInfo rvbar = { + .name = "RVBAR_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .crn = 12, .crm = 0, .opc2 = 1, + .type = ARM_CP_CONST, .access = PL3_R, .resetvalue = cpu->rvbar + }; + define_one_arm_cp_reg(cpu, &rvbar); } if (arm_feature(env, ARM_FEATURE_MPU)) { /* These are the MPU registers prior to PMSAv6. Any new @@ -3440,14 +3586,14 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, */ if ((r->state == ARM_CP_STATE_BOTH && ns) || (arm_feature(&cpu->env, ARM_FEATURE_V8) && !ns)) { - r2->type |= ARM_CP_NO_MIGRATE; + r2->type |= ARM_CP_ALIAS; r2->resetfn = arm_cp_reset_ignore; } } else if ((secstate != r->secure) && !ns) { /* The register is not banked so we only want to allow migration of * the non-secure instance. */ - r2->type |= ARM_CP_NO_MIGRATE; + r2->type |= ARM_CP_ALIAS; r2->resetfn = arm_cp_reset_ignore; } @@ -3496,15 +3642,25 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->opc2 = opc2; /* By convention, for wildcarded registers only the first * entry is used for migration; the others are marked as - * NO_MIGRATE so we don't try to transfer the register + * ALIAS so we don't try to transfer the register * multiple times. Special registers (ie NOP/WFI) are - * never migratable. + * never migratable and not even raw-accessible. */ - if ((r->type & ARM_CP_SPECIAL) || - ((r->crm == CP_ANY) && crm != 0) || + if ((r->type & ARM_CP_SPECIAL)) { + r2->type |= ARM_CP_NO_RAW; + } + if (((r->crm == CP_ANY) && crm != 0) || ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { - r2->type |= ARM_CP_NO_MIGRATE; + r2->type |= ARM_CP_ALIAS; + } + + /* Check that raw accesses are either forbidden or handled. Note that + * we can't assert this earlier because the setup of fieldoffset for + * banked registers has to be done first. + */ + if (!(r2->type & ARM_CP_NO_RAW)) { + assert(!raw_accessors_invalid(r2)); } /* Overriding of an existing definition must be explicitly @@ -4460,91 +4616,170 @@ void arm_cpu_do_interrupt(CPUState *cs) cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } + +/* Return the exception level which controls this address translation regime */ +static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_S2NS: + case ARMMMUIdx_S1E2: + return 2; + case ARMMMUIdx_S1E3: + return 3; + case ARMMMUIdx_S1SE0: + return arm_el_is_aa64(env, 3) ? 1 : 3; + case ARMMMUIdx_S1SE1: + case ARMMMUIdx_S1NSE0: + case ARMMMUIdx_S1NSE1: + return 1; + default: + g_assert_not_reached(); + } +} + +/* Return the SCTLR value which controls this address translation regime */ +static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + return env->cp15.sctlr_el[regime_el(env, mmu_idx)]; +} + +/* Return true if the specified stage of address translation is disabled */ +static inline bool regime_translation_disabled(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S2NS) { + return (env->cp15.hcr_el2 & HCR_VM) == 0; + } + return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; +} + +/* Return the TCR controlling this translation regime */ +static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + if (mmu_idx == ARMMMUIdx_S2NS) { + /* TODO: return VTCR_EL2 */ + g_assert_not_reached(); + } + return &env->cp15.tcr_el[regime_el(env, mmu_idx)]; +} + +/* Return true if the translation regime is using LPAE format page tables */ +static inline bool regime_using_lpae_format(CPUARMState *env, + ARMMMUIdx mmu_idx) +{ + int el = regime_el(env, mmu_idx); + if (el == 2 || arm_el_is_aa64(env, el)) { + return true; + } + if (arm_feature(env, ARM_FEATURE_LPAE) + && (regime_tcr(env, mmu_idx)->raw_tcr & TTBCR_EAE)) { + return true; + } + return false; +} + +static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + switch (mmu_idx) { + case ARMMMUIdx_S1SE0: + case ARMMMUIdx_S1NSE0: + return true; + default: + return false; + case ARMMMUIdx_S12NSE0: + case ARMMMUIdx_S12NSE1: + g_assert_not_reached(); + } +} + /* Check section/page access permissions. Returns the page protection flags, or zero if the access is not permitted. */ -static inline int check_ap(CPUARMState *env, int ap, int domain_prot, - int access_type, int is_user) -{ - int prot_ro; - - if (domain_prot == 3) { - return PAGE_READ | PAGE_WRITE; - } - - if (access_type == 1) - prot_ro = 0; - else - prot_ro = PAGE_READ; - - switch (ap) { - case 0: - if (arm_feature(env, ARM_FEATURE_V7)) { - return 0; - } - if (access_type == 1) - return 0; - switch (A32_BANKED_CURRENT_REG_GET(env, sctlr) & (SCTLR_S | SCTLR_R)) { - case SCTLR_S: - return is_user ? 0 : PAGE_READ; - case SCTLR_R: - return PAGE_READ; - default: - return 0; - } - case 1: - return is_user ? 0 : PAGE_READ | PAGE_WRITE; - case 2: - if (is_user) - return prot_ro; - else - return PAGE_READ | PAGE_WRITE; - case 3: - return PAGE_READ | PAGE_WRITE; - case 4: /* Reserved. */ - return 0; - case 5: - return is_user ? 0 : prot_ro; - case 6: - return prot_ro; - case 7: - if (!arm_feature (env, ARM_FEATURE_V6K)) - return 0; - return prot_ro; - default: - abort(); - } -} - -static bool get_level1_table_address(CPUARMState *env, uint32_t *table, - uint32_t address) -{ - /* Get the TCR bank based on our security state */ - TCR *tcr = &env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; - - /* We only get here if EL1 is running in AArch32. If EL3 is running in - * AArch32 there is a secure and non-secure instance of the translation - * table registers. - */ +static inline int check_ap(CPUARMState *env, ARMMMUIdx mmu_idx, + int ap, int domain_prot, + int access_type) +{ + int prot_ro; + bool is_user = regime_is_user(env, mmu_idx); + + if (domain_prot == 3) { + return PAGE_READ | PAGE_WRITE; + } + + if (access_type == 1) { + prot_ro = 0; + } else { + prot_ro = PAGE_READ; + } + + switch (ap) { + case 0: + if (arm_feature(env, ARM_FEATURE_V7)) { + return 0; + } + if (access_type == 1) { + return 0; + } + switch (regime_sctlr(env, mmu_idx) & (SCTLR_S | SCTLR_R)) { + case SCTLR_S: + return is_user ? 0 : PAGE_READ; + case SCTLR_R: + return PAGE_READ; + default: + return 0; + } + case 1: + return is_user ? 0 : PAGE_READ | PAGE_WRITE; + case 2: + if (is_user) { + return prot_ro; + } else { + return PAGE_READ | PAGE_WRITE; + } + case 3: + return PAGE_READ | PAGE_WRITE; + case 4: /* Reserved. */ + return 0; + case 5: + return is_user ? 0 : prot_ro; + case 6: + return prot_ro; + case 7: + if (!arm_feature(env, ARM_FEATURE_V6K)) { + return 0; + } + return prot_ro; + default: + abort(); + } +} + +static bool get_level1_table_address(CPUARMState *env, ARMMMUIdx mmu_idx, + uint32_t *table, uint32_t address) +{ + /* Note that we can only get here for an AArch32 PL0/PL1 lookup */ + int el = regime_el(env, mmu_idx); + TCR *tcr = regime_tcr(env, mmu_idx); + if (address & tcr->mask) { if (tcr->raw_tcr & TTBCR_PD1) { /* Translation table walk disabled for TTBR1 */ return false; } - *table = A32_BANKED_CURRENT_REG_GET(env, ttbr1) & 0xffffc000; + *table = env->cp15.ttbr1_el[el] & 0xffffc000; } else { if (tcr->raw_tcr & TTBCR_PD0) { /* Translation table walk disabled for TTBR0 */ return false; } - *table = A32_BANKED_CURRENT_REG_GET(env, ttbr0) & tcr->base_mask; + *table = env->cp15.ttbr0_el[el] & tcr->base_mask; } *table |= (address >> 18) & 0x3ffc; return true; } static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, - int is_user, hwaddr *phys_ptr, + ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -4556,10 +4791,11 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, int domain = 0; int domain_prot; hwaddr phys_addr; + uint32_t dacr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ - if (!get_level1_table_address(env, &table, address)) { + if (!get_level1_table_address(env, mmu_idx, &table, address)) { /* Section translation fault if page walk is disabled by PD0 or PD1 */ code = 5; goto do_fault; @@ -4567,7 +4803,12 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, desc = ldl_phys(cs->as, table); type = (desc & 3); domain = (desc >> 5) & 0x0f; - domain_prot = (A32_BANKED_CURRENT_REG_GET(env, dacr) >> (domain * 2)) & 3; + if (regime_el(env, mmu_idx) == 1) { + dacr = env->cp15.dacr_ns; + } else { + dacr = env->cp15.dacr_s; + } + domain_prot = (dacr >> (domain * 2)) & 3; if (type == 0) { /* Section translation fault. */ code = 5; @@ -4588,13 +4829,13 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, *page_size = 1024 * 1024; } else { /* Lookup l2 entry. */ - if (type == 1) { - /* Coarse pagetable. */ - table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); - } else { - /* Fine pagetable. */ - table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); - } + if (type == 1) { + /* Coarse pagetable. */ + table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); + } else { + /* Fine pagetable. */ + table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); + } desc = ldl_phys(cs->as, table); switch (desc & 3) { case 0: /* Page translation fault. */ @@ -4611,17 +4852,17 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, *page_size = 0x1000; break; case 3: /* 1k page. */ - if (type == 1) { - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - phys_addr = (desc & 0xfffff000) | (address & 0xfff); - } else { - /* Page translation fault. */ - code = 7; - goto do_fault; - } - } else { - phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); - } + if (type == 1) { + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + } else { + /* Page translation fault. */ + code = 7; + goto do_fault; + } + } else { + phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); + } ap = (desc >> 4) & 3; *page_size = 0x400; break; @@ -4631,7 +4872,7 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, } code = 15; } - *prot = check_ap(env, ap, domain_prot, access_type, is_user); + *prot = check_ap(env, mmu_idx, ap, domain_prot, access_type); if (!*prot) { /* Access permission fault. */ goto do_fault; @@ -4644,7 +4885,7 @@ do_fault: } static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, - int is_user, hwaddr *phys_ptr, + ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { CPUState *cs = CPU(arm_env_get_cpu(env)); @@ -4658,10 +4899,11 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, int domain = 0; int domain_prot; hwaddr phys_addr; + uint32_t dacr; /* Pagetable walk. */ /* Lookup l1 descriptor. */ - if (!get_level1_table_address(env, &table, address)) { + if (!get_level1_table_address(env, mmu_idx, &table, address)) { /* Section translation fault if page walk is disabled by PD0 or PD1 */ code = 5; goto do_fault; @@ -4679,7 +4921,12 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, /* Page or Section. */ domain = (desc >> 5) & 0x0f; } - domain_prot = (A32_BANKED_CURRENT_REG_GET(env, dacr) >> (domain * 2)) & 3; + if (regime_el(env, mmu_idx) == 1) { + dacr = env->cp15.dacr_ns; + } else { + dacr = env->cp15.dacr_s; + } + domain_prot = (dacr >> (domain * 2)) & 3; if (domain_prot == 0 || domain_prot == 2) { if (type != 1) { code = 9; /* Section domain fault. */ @@ -4733,20 +4980,20 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, if (domain_prot == 3) { *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; } else { - if (pxn && !is_user) { + if (pxn && !regime_is_user(env, mmu_idx)) { xn = 1; } if (xn && access_type == 2) goto do_fault; /* The simplified model uses AP[0] as an access control bit. */ - if ((A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_AFE) + if ((regime_sctlr(env, mmu_idx) & SCTLR_AFE) && (ap & 1) == 0) { /* Access flag fault. */ code = (code == 15) ? 6 : 3; goto do_fault; } - *prot = check_ap(env, ap, domain_prot, access_type, is_user); + *prot = check_ap(env, mmu_idx, ap, domain_prot, access_type); if (!*prot) { /* Access permission fault. */ goto do_fault; @@ -4771,7 +5018,7 @@ typedef enum { } MMUFaultType; static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size_ptr) { @@ -4791,9 +5038,17 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, int32_t granule_sz = 9; int32_t va_size = 32; int32_t tbi = 0; - TCR *tcr = &env->cp15.tcr_el[arm_is_secure(env) ? 3 : 1]; - - if (arm_el_is_aa64(env, 1)) { + bool is_user; + TCR *tcr = regime_tcr(env, mmu_idx); + + /* TODO: + * This code assumes we're either a 64-bit EL1 or a 32-bit PL1; + * it doesn't handle the different format TCR for TCR_EL2, TCR_EL3, + * and VTCR_EL2, or the fact that those regimes don't have a split + * TTBR0/TTBR1. Attribute and permission bit handling should also + * be checked when adding support for those page table walks. + */ + if (arm_el_is_aa64(env, regime_el(env, mmu_idx))) { va_size = 64; if (extract64(address, 55, 1)) tbi = extract64(tcr->raw_tcr, 38, 1); @@ -4808,12 +5063,12 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, * TTBCR/TTBR0/TTBR1 in accordance with ARM ARM DDI0406C table B-32: */ uint32_t t0sz = extract32(tcr->raw_tcr, 0, 6); - if (arm_el_is_aa64(env, 1)) { + if (va_size == 64) { t0sz = MIN(t0sz, 39); t0sz = MAX(t0sz, 16); } uint32_t t1sz = extract32(tcr->raw_tcr, 16, 6); - if (arm_el_is_aa64(env, 1)) { + if (va_size == 64) { t1sz = MIN(t1sz, 39); t1sz = MAX(t1sz, 16); } @@ -4868,6 +5123,10 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, } } + /* Here we should have set up all the parameters for the translation: + * va_size, ttbr, epd, tsz, granule_sz, tbi + */ + if (epd) { /* Translation table walk disabled => Translation fault on TLB miss */ goto do_fault; @@ -4953,6 +5212,7 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, goto do_fault; } fault_type = permission_fault; + is_user = regime_is_user(env, mmu_idx); if (is_user && !(attrs & (1 << 4))) { /* Unprivileged access not enabled */ goto do_fault; @@ -4987,27 +5247,31 @@ do_fault: } static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot) { int n; uint32_t mask; uint32_t base; + bool is_user = regime_is_user(env, mmu_idx); *phys_ptr = address; for (n = 7; n >= 0; n--) { - base = env->cp15.c6_region[n]; - if ((base & 1) == 0) - continue; - mask = 1 << ((base >> 1) & 0x1f); - /* Keep this shift separate from the above to avoid an - (undefined) << 32. */ - mask = (mask << 1) - 1; - if (((base ^ address) & ~mask) == 0) - break; - } - if (n < 0) - return 2; + base = env->cp15.c6_region[n]; + if ((base & 1) == 0) { + continue; + } + mask = 1 << ((base >> 1) & 0x1f); + /* Keep this shift separate from the above to avoid an + (undefined) << 32. */ + mask = (mask << 1) - 1; + if (((base ^ address) & ~mask) == 0) { + break; + } + } + if (n < 0) { + return 2; + } if (access_type == 2) { mask = env->cp15.pmsav5_insn_ap; @@ -5017,31 +5281,34 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, mask = (mask >> (n * 4)) & 0xf; switch (mask) { case 0: - return 1; + return 1; case 1: - if (is_user) - return 1; - *prot = PAGE_READ | PAGE_WRITE; - break; + if (is_user) { + return 1; + } + *prot = PAGE_READ | PAGE_WRITE; + break; case 2: - *prot = PAGE_READ; - if (!is_user) - *prot |= PAGE_WRITE; - break; + *prot = PAGE_READ; + if (!is_user) { + *prot |= PAGE_WRITE; + } + break; case 3: - *prot = PAGE_READ | PAGE_WRITE; - break; + *prot = PAGE_READ | PAGE_WRITE; + break; case 5: - if (is_user) - return 1; - *prot = PAGE_READ; - break; + if (is_user) { + return 1; + } + *prot = PAGE_READ; + break; case 6: - *prot = PAGE_READ; - break; + *prot = PAGE_READ; + break; default: - /* Bad permission. */ - return 1; + /* Bad permission. */ + return 1; } *prot |= PAGE_EXEC; return 0; @@ -5065,44 +5332,60 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, * @env: CPUARMState * @address: virtual address to get physical address for * @access_type: 0 for read, 1 for write, 2 for execute - * @is_user: 0 for privileged access, 1 for user + * @mmu_idx: MMU index indicating required translation regime * @phys_ptr: set to the physical address corresponding to the virtual address * @prot: set to the permissions for the page containing phys_ptr * @page_size: set to the size of the page containing phys_ptr */ static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, int is_user, + int access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, target_ulong *page_size) { - /* This is not entirely correct as get_phys_addr() can also be called - * from ats_write() for an address translation of a specific regime. - */ - uint32_t sctlr = A32_BANKED_CURRENT_REG_GET(env, sctlr); + if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { + /* TODO: when we support EL2 we should here call ourselves recursively + * to do the stage 1 and then stage 2 translations. The ldl_phys + * calls for stage 1 will also need changing. + * For non-EL2 CPUs a stage1+stage2 translation is just stage 1. + */ + assert(!arm_feature(env, ARM_FEATURE_EL2)); + mmu_idx += ARMMMUIdx_S1NSE0; + } - /* Fast Context Switch Extension. */ - if (address < 0x02000000) { - address += A32_BANKED_CURRENT_REG_GET(env, fcseidr); + /* Fast Context Switch Extension. This doesn't exist at all in v8. + * In v7 and earlier it affects all stage 1 translations. + */ + if (address < 0x02000000 && mmu_idx != ARMMMUIdx_S2NS + && !arm_feature(env, ARM_FEATURE_V8)) { + if (regime_el(env, mmu_idx) == 3) { + address += env->cp15.fcseidr_s; + } else { + address += env->cp15.fcseidr_ns; + } } - if ((sctlr & SCTLR_M) == 0) { + if (regime_translation_disabled(env, mmu_idx)) { /* MMU/MPU disabled. */ *phys_ptr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *page_size = TARGET_PAGE_SIZE; return 0; - } else if (arm_feature(env, ARM_FEATURE_MPU)) { + } + + if (arm_feature(env, ARM_FEATURE_MPU)) { *page_size = TARGET_PAGE_SIZE; - return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr, - prot); - } else if (extended_addresses_enabled(env)) { - return get_phys_addr_lpae(env, address, access_type, is_user, phys_ptr, + return get_phys_addr_mpu(env, address, access_type, mmu_idx, phys_ptr, + prot); + } + + if (regime_using_lpae_format(env, mmu_idx)) { + return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); - } else if (sctlr & SCTLR_XP) { - return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr, + } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) { + return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); } else { - return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr, + return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr, prot, page_size); } } @@ -5115,12 +5398,11 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, hwaddr phys_addr; target_ulong page_size; int prot; - int ret, is_user; + int ret; uint32_t syn; bool same_el = (arm_current_el(env) != 0); - is_user = mmu_idx == MMU_USER_IDX; - ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot, + ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr, &prot, &page_size); if (ret == 0) { /* Map a single [sub]page. */ @@ -5156,12 +5438,14 @@ int arm_cpu_handle_mmu_fault(CPUState *cs, vaddr address, hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; hwaddr phys_addr; target_ulong page_size; int prot; int ret; - ret = get_phys_addr(&cpu->env, addr, 0, 0, &phys_addr, &prot, &page_size); + ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr, + &prot, &page_size); if (ret != 0) { return -1; @@ -6242,7 +6526,7 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) } else { return float64_set_sign(float64_maxnorm, float64_is_neg(f64)); } - } else if (f64_exp >= 1023 && fpst->flush_to_zero) { + } else if (f64_exp >= 2045 && fpst->flush_to_zero) { float_raise(float_flag_underflow, fpst); return float64_set_sign(float64_zero, float64_is_neg(f64)); } |