diff options
Diffstat (limited to 'target/arm/helper.c')
| -rw-r--r-- | target/arm/helper.c | 183 |
1 files changed, 120 insertions, 63 deletions
diff --git a/target/arm/helper.c b/target/arm/helper.c index 66afb08ee0..c9bce1efcb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -444,9 +444,11 @@ static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tdosa = (env->cp15.mdcr_el2 & MDCR_TDOSA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (env->cp15.hcr_el2 & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDOSA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tdosa && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { @@ -462,9 +464,11 @@ static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tdra = (env->cp15.mdcr_el2 & MDCR_TDRA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (env->cp15.hcr_el2 & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDRA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tdra && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { @@ -480,9 +484,11 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tda = (env->cp15.mdcr_el2 & MDCR_TDA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (env->cp15.hcr_el2 & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tda && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { @@ -3744,7 +3750,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* Used to describe the behaviour of EL2 regs when EL2 does not exist. */ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, @@ -3753,6 +3759,10 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3761,14 +3771,14 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 0, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, @@ -3837,6 +3847,13 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_CONST, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -3882,18 +3899,23 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .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, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, - { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.far_el[2]) }, { .name = "SPSR_EL2", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 4, .crn = 4, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, banked_spsr[BANK_HYP]) }, - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]), @@ -3911,7 +3933,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]), .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetofhigh32(CPUARMState, cp15.mair_el[2]) }, { .name = "AMAIR_EL2", .state = ARM_CP_STATE_BOTH, @@ -3919,8 +3941,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, /* HAMAIR1 is mapped to AMAIR_EL2[63:32] */ - { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 10, .crm = 3, .opc2 = 1, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "AFSR0_EL2", .state = ARM_CP_STATE_BOTH, @@ -6330,15 +6352,15 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, switch (excp_idx) { case EXCP_IRQ: scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); - hcr = ((env->cp15.hcr_el2 & HCR_IMO) == HCR_IMO); + hcr = arm_hcr_el2_imo(env); break; case EXCP_FIQ: scr = ((env->cp15.scr_el3 & SCR_FIQ) == SCR_FIQ); - hcr = ((env->cp15.hcr_el2 & HCR_FMO) == HCR_FMO); + hcr = arm_hcr_el2_fmo(env); break; default: scr = ((env->cp15.scr_el3 & SCR_EA) == SCR_EA); - hcr = ((env->cp15.hcr_el2 & HCR_AMO) == HCR_AMO); + hcr = arm_hcr_el2_amo(env); break; }; @@ -6834,6 +6856,8 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, bool push_failed = false; armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure); + qemu_log_mask(CPU_LOG_INT, "...taking pending %s exception %d\n", + targets_secure ? "secure" : "nonsecure", exc); if (arm_feature(env, ARM_FEATURE_V8)) { if (arm_feature(env, ARM_FEATURE_M_SECURITY) && @@ -6907,12 +6931,15 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, * we might now want to take a different exception which * targets a different security state, so try again from the top. */ + qemu_log_mask(CPU_LOG_INT, + "...derived exception on callee-saves register stacking"); v7m_exception_taken(cpu, lr, true, true); return; } if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) { /* Vector load failed: derived exception */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on vector table load"); v7m_exception_taken(cpu, lr, true, true); return; } @@ -7041,6 +7068,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* For all other purposes, treat ES as 0 (R_HXSR) */ excret &= ~R_V7M_EXCRET_ES_MASK; } + exc_secure = excret & R_V7M_EXCRET_ES_MASK; } if (env->v7m.exception != ARMV7M_EXCP_NMI) { @@ -7051,7 +7079,6 @@ static void do_v7m_exception_exit(ARMCPU *cpu) * which security state's faultmask to clear. (v8M ARM ARM R_KBNF.) */ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - exc_secure = excret & R_V7M_EXCRET_ES_MASK; if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) { env->v7m.faultmask[exc_secure] = 0; } @@ -7120,12 +7147,22 @@ static void do_v7m_exception_exit(ARMCPU *cpu) } } + /* + * Set CONTROL.SPSEL from excret.SPSEL. Since we're still in + * Handler mode (and will be until we write the new XPSR.Interrupt + * field) this does not switch around the current stack pointer. + * We must do this before we do any kind of tailchaining, including + * for the derived exceptions on integrity check failures, or we will + * give the guest an incorrect EXCRET.SPSEL value on exception entry. + */ + write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); + if (sfault) { env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " "stackframe: failed EXC_RETURN.ES validity check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } @@ -7135,17 +7172,27 @@ static void do_v7m_exception_exit(ARMCPU *cpu) */ env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " "stackframe: failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } - /* Set CONTROL.SPSEL from excret.SPSEL. Since we're still in - * Handler mode (and will be until we write the new XPSR.Interrupt - * field) this does not switch around the current stack pointer. - */ - write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); + /* + * Tailchaining: if there is currently a pending exception that + * is high enough priority to preempt execution at the level we're + * about to return to, then just directly take that exception now, + * avoiding an unstack-and-then-stack. Note that now we have + * deactivated the previous exception by calling armv7m_nvic_complete_irq() + * our current execution priority is already the execution priority we are + * returning to -- none of the state we would unstack or set based on + * the EXCRET value affects it. + */ + if (armv7m_nvic_can_take_pending_exception(env->nvic)) { + qemu_log_mask(CPU_LOG_INT, "...tailchaining to pending exception\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } switch_v7m_security_state(env, return_to_secure); @@ -7192,10 +7239,10 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* Take a SecureFault on the current stack */ env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " "stackframe: failed exception return integrity " "signature check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } @@ -7228,6 +7275,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* v7m_stack_read() pended a fault, so take it (as a tail * chained exception on the same stack frame) */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on unstacking\n"); v7m_exception_taken(cpu, excret, true, false); return; } @@ -7264,10 +7312,10 @@ static void do_v7m_exception_exit(ARMCPU *cpu) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " "stackframe: failed exception return integrity " "check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } } @@ -7303,9 +7351,9 @@ static void do_v7m_exception_exit(ARMCPU *cpu) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; ignore_stackfaults = v7m_push_stack(cpu); - v7m_exception_taken(cpu, excret, false, ignore_stackfaults); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: " "failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, false, ignore_stackfaults); return; } @@ -7721,7 +7769,6 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) ignore_stackfaults = v7m_push_stack(cpu); v7m_exception_taken(cpu, lr, false, ignore_stackfaults); - qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception); } /* Function used to synchronize QEMU's AArch64 register set with AArch32 @@ -8390,6 +8437,14 @@ static inline bool regime_translation_disabled(CPUARMState *env, if (mmu_idx == ARMMMUIdx_S2NS) { return (env->cp15.hcr_el2 & HCR_VM) == 0; } + + if (env->cp15.hcr_el2 & HCR_TGE) { + /* TGE means that NS EL0/1 act as if SCTLR_EL1.M is zero */ + if (!regime_is_secure(env, mmu_idx) && regime_el(env, mmu_idx) == 1) { + return true; + } + } + return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } @@ -9795,17 +9850,6 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; - /* - * Core QEMU code can't handle execution from small pages yet, so - * don't try it. This way we'll get an MPU exception, rather than - * eventually causing QEMU to exit in get_page_addr_code(). - */ - if (*page_size < TARGET_PAGE_SIZE && (*prot & PAGE_EXEC)) { - qemu_log_mask(LOG_UNIMP, - "MPU: No support for execution from regions " - "smaller than 1K\n"); - *prot &= ~PAGE_EXEC; - } return !(*prot & (1 << access_type)); } @@ -10056,18 +10100,6 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; - /* - * Core QEMU code can't handle execution from small pages yet, so - * don't try it. This means any attempted execution will generate - * an MPU exception, rather than eventually causing QEMU to exit in - * get_page_addr_code(). - */ - if (*is_subpage && (*prot & PAGE_EXEC)) { - qemu_log_mask(LOG_UNIMP, - "MPU: No support for execution from regions " - "smaller than 1K\n"); - *prot &= ~PAGE_EXEC; - } return !(*prot & (1 << access_type)); } @@ -10710,13 +10742,13 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.primask[M_REG_NS] = val & 1; return; case 0x91: /* BASEPRI_NS */ - if (!env->v7m.secure) { + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { return; } env->v7m.basepri[M_REG_NS] = val & 0xff; return; case 0x93: /* FAULTMASK_NS */ - if (!env->v7m.secure) { + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { return; } env->v7m.faultmask[M_REG_NS] = val & 1; @@ -10728,8 +10760,10 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) write_v7m_control_spsel_for_secstate(env, val & R_V7M_CONTROL_SPSEL_MASK, M_REG_NS); - env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; + } return; case 0x98: /* SP_NS */ { @@ -10798,9 +10832,15 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.primask[env->v7m.secure] = val & 1; break; case 17: /* BASEPRI */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } env->v7m.basepri[env->v7m.secure] = val & 0xff; break; case 18: /* BASEPRI_MAX */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } val &= 0xff; if (val != 0 && (val < env->v7m.basepri[env->v7m.secure] || env->v7m.basepri[env->v7m.secure] == 0)) { @@ -10808,6 +10848,9 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) } break; case 19: /* FAULTMASK */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } env->v7m.faultmask[env->v7m.secure] = val & 1; break; case 20: /* CONTROL */ @@ -10822,8 +10865,10 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) !arm_v7m_is_handler_mode(env)) { write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); } - env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; + } break; default: bad_reg: @@ -11283,9 +11328,13 @@ uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) fpscr = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff) | (env->vfp.vec_len << 16) | (env->vfp.vec_stride << 20); + i = get_float_exception_flags(&env->vfp.fp_status); i |= get_float_exception_flags(&env->vfp.standard_fp_status); - i |= get_float_exception_flags(&env->vfp.fp_status_f16); + /* FZ16 does not generate an input denormal exception. */ + i |= (get_float_exception_flags(&env->vfp.fp_status_f16) + & ~float_flag_input_denormal); + fpscr |= vfp_exceptbits_from_host(i); return fpscr; } @@ -11320,6 +11369,11 @@ void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) int i; uint32_t changed; + /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ + if (!arm_feature(env, ARM_FEATURE_V8_FP16)) { + val &= ~FPCR_FZ16; + } + changed = env->vfp.xregs[ARM_VFP_FPSCR]; env->vfp.xregs[ARM_VFP_FPSCR] = (val & 0xffc8ffff); env->vfp.vec_len = (val >> 16) & 7; @@ -12408,9 +12462,12 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, zcr_len = 0; } else { int current_el = arm_current_el(env); + ARMCPU *cpu = arm_env_get_cpu(env); - zcr_len = env->vfp.zcr_el[current_el <= 1 ? 1 : current_el]; - zcr_len &= 0xf; + zcr_len = cpu->sve_max_vq - 1; + if (current_el <= 1) { + zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[1]); + } if (current_el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[2]); } |